288 _result.keys = _in.keys;
289 _result.keys.resize(_in.columns.size());
301 return run(deadline, stop);
302 }
catch (std::exception
const& error) {
303 _failure = error.what();
305 _failure =
"unknown exception";
307 _phase = Phase::Done;
314 void onPhase(std::function<
void(std::size_t, std::size_t)> listener) { _onPhase = std::move(listener); }
318 [[nodiscard]] std::pair<std::size_t, std::size_t>
progress() const noexcept {
319 return {
static_cast<std::size_t
>(_phase),
static_cast<std::size_t
>(Phase::Done)};
324 [[nodiscard]]
bool done() const noexcept {
return _phase == Phase::Done; }
328 [[nodiscard]] std::optional<std::string>
const&
failure() const noexcept {
return _failure; }
339 enum class Phase : std::uint8_t { Keys, Order, Diff, Done };
341 bool run(
Deadline deadline, ::core::async::StopToken
const& stop) {
342 while (_phase != Phase::Done) {
343 if (stop.stop_requested()) {
346 bool finished =
false;
349 finished = buildKeys(deadline, stop);
352 finished = order(deadline, stop);
368 auto const [doneUnits, total] =
progress();
369 _onPhase(doneUnits, total);
371 if (_phase != Phase::Done && detail::shouldYield(deadline, stop)) {
381 _phase = Phase::Order;
384 _phase = Phase::Diff;
389 _phase = Phase::Done;
397 bool buildKeys(Deadline deadline, ::core::async::StopToken
const& stop) {
399 auto const rows = _in.snapshot->rowCount();
400 if (!_patched && !_in.dirty.empty()) {
401 for (std::size_t column = 0;
column < _result.keys.size(); ++
column) {
402 if (_result.keys[column] ==
nullptr) {
405 auto copy = std::make_shared<KeyColumn>(*_result.keys[column]);
406 for (
auto const row : _in.dirty) {
407 copy->set(row, _in.snapshot->cell(row, column), _in.columns[column], _in.services);
409 _result.keys[
column] = std::move(copy);
413 while (_neededAt < _in.needed.size()) {
414 auto const column = _in.needed[_neededAt];
415 if (_result.keys[column] !=
nullptr && _building ==
nullptr) {
419 if (_building ==
nullptr) {
420 _building = std::make_shared<KeyColumn>(
KeyColumn::forColumn(_in.columns[column], _in.services, rows));
423 while (_rowAt < rows) {
424 auto const end = std::min(_rowAt + detail::kCheckEvery, rows);
428 if (_rowAt < rows && detail::shouldYield(deadline, stop)) {
432 _result.keys[
column] = std::move(_building);
441 [[nodiscard]] std::string describeSort()
const {
443 for (
auto const& key : _in.sort) {
445 out += key.dir == SortDirection::Descending ?
" desc " :
" asc ";
449 [[nodiscard]] std::string describeFilter()
const {
450 return _in.filter ==
nullptr ? std::string{}
451 : std::to_string(_in.filter->columns().size()) +
" columns, " +
452 std::to_string(_in.filter->groups().size()) +
" groups";
455 [[nodiscard]] RowOrder rowOrder()
const {
456 std::vector<RowOrder::Key> keys;
457 for (
auto const& key : _in.sort) {
458 if (
auto const column =
findColumn(_in.columns, key.column)) {
459 keys.push_back({.keys = _result.keys[*
column].get(), .dir = key.dir});
462 return RowOrder{std::move(keys)};
465 [[nodiscard]] std::vector<KeyColumn const*> keyPointers()
const {
466 std::vector<KeyColumn const*> out;
467 out.reserve(_result.keys.size());
468 for (
auto const& keys : _result.keys) {
469 out.push_back(keys.get());
474 [[nodiscard]]
bool passesFilter(std::uint32_t row)
const {
475 if (_in.filter ==
nullptr) {
478 for (
auto const& column : _in.filter->columns()) {
483 for (
auto const& group : _in.filter->groups()) {
484 bool const any = std::ranges::any_of(group.columns, [&](CompiledFilter::Column
const& column) {
485 return CompiledFilter::passes(column, *_result.keys[column.column], row);
495 [[nodiscard]] std::vector<std::uint32_t> repairOrder(std::span<std::uint32_t const> previous,
496 std::vector<std::uint32_t> dirtyRows,
497 RowOrder
const& orderBy)
const {
498 std::vector<bool> isDirty(_in.snapshot->rowCount(),
false);
499 for (
auto const row : _in.dirty) {
502 std::vector<std::uint32_t> clean;
503 clean.reserve(previous.size());
504 for (
auto const row : previous) {
506 clean.push_back(row);
509 auto const sortedDirty =
sortRows(std::move(dirtyRows), orderBy);
510 std::vector<std::uint32_t> out(clean.size() + sortedDirty.size());
511 std::ranges::merge(clean, sortedDirty, out.begin(), orderBy);
519 auto const orderBy = rowOrder();
520 std::vector<std::uint32_t> passing;
521 for (
auto const row : _in.dirty) {
522 if (passesFilter(row)) {
523 passing.push_back(row);
526 _result.view = repairOrder(_in.previousTrueView, std::move(passing), orderBy);
527 if (_in.cachedSorted !=
nullptr) {
529 std::make_shared<std::vector<std::uint32_t>
const>(repairOrder(*_in.cachedSorted, _in.dirty, orderBy));
531 _result.repaired =
true;
538 auto const rows = _in.snapshot->rowCount();
539 auto const filtering = _in.filter !=
nullptr && !_in.filter->empty();
541 if (_in.cachedSorted !=
nullptr) {
542 if (_in.dirty.empty()) {
543 _base = *_in.cachedSorted;
544 _result.sorted = _in.cachedSorted;
546 _base = repairOrder(*_in.cachedSorted, _in.dirty, rowOrder());
547 _result.sorted = std::make_shared<std::vector<std::uint32_t>
const>(_base);
553 std::iota(_base.begin(), _base.end(), 0U);
554 _sorted = _in.sort.empty();
555 if (!_sorted && !filtering) {
556 _sort.emplace(std::move(_base), rowOrder());
557 _result.fullSort =
true;
560 if (filtering && (_sorted || _sort == std::nullopt)) {
561 _filterRun.emplace(*_in.filter, keyPointers(), std::move(_base));
565 bool order(Deadline deadline, ::core::async::StopToken
const& stop) {
576 if (!_filterRun->run(deadline, stop)) {
579 _base = std::move(*_filterRun).take();
583 _sort.emplace(std::move(_base), rowOrder());
584 _result.subsetSort =
true;
590 if (!_sort->run(deadline, stop)) {
593 _base = std::move(*_sort).take();
596 if (_result.fullSort) {
597 _result.sorted = std::make_shared<std::vector<std::uint32_t>
const>(_base);
600 _result.view = std::move(_base);
606 std::vector<std::uint32_t> changed = _in.dirty;
607 if (_in.indicesStable) {
608 if (_in.allChanged) {
609 changed = _result.view;
612 diffViews(_in.previousView, _result.view, changed, _in.snapshot->rowCount(), _in.resetThreshold);
617 auto const rows = _in.snapshot->rowCount();
618 std::unordered_map<RowId, std::uint32_t> byKey;
620 for (std::size_t row = 0;
row < rows; ++
row) {
621 byKey.emplace(_in.snapshot->rowId(row),
static_cast<std::uint32_t
>(row));
623 std::vector<std::uint32_t> previous;
624 previous.reserve(_in.previousView.size());
625 auto next =
static_cast<std::uint32_t
>(rows);
626 for (
auto const row : _in.previousView) {
627 auto const found = byKey.find(_in.previousSnapshot->rowId(row));
628 previous.push_back(found != byKey.end() ? found->second : next++);
630 if (_in.allChanged) {
631 changed = _result.view;
633 _result.change =
diffViews(previous, _result.view, changed, next, _in.resetThreshold);
638 Phase _phase = Phase::Keys;
640 bool _patched =
false;
641 std::size_t _neededAt = 0;
642 std::size_t _rowAt = 0;
643 std::shared_ptr<KeyColumn> _building;
645 bool _started =
false;
646 bool _sorted =
false;
647 std::vector<std::uint32_t> _base;
648 std::optional<FilterRun> _filterRun;
649 std::optional<ChunkedMergeSort> _sort;
651 std::optional<std::string> _failure;
652 std::function<void(std::size_t, std::size_t)> _onPhase;
667 : _source{std::move(source)},
668 _options{std::move(options)},
670 _snapshot{_source->snapshot()},
671 _viewColumns{_columns},
672 _viewSnapshot{_snapshot},
673 _view(identity(_snapshot->rowCount())),
675 _keysSnapshot{_snapshot},
676 _keys(_columns.size()) {
677 _source->subscribe(*
this);
687 _jobStop.request_stop();
688 _source->unsubscribe(*
this);
700 for (
auto const& key : chain) {
705 if (chain == _sort) {
708 _sort = std::move(chain);
718 auto compiled = compile(spec);
720 return std::unexpected(std::move(compiled.error()));
722 _filterSpec = std::move(spec);
723 _filter = std::make_shared<CompiledFilter const>(std::move(*compiled));
739 [[nodiscard]] std::span<ColumnInfo const>
columns() const noexcept {
return _viewColumns; }
743 [[nodiscard]] std::size_t
viewRowCount() const noexcept {
return _view.size(); }
747 [[nodiscard]] std::size_t
sourceRowCount()
const {
return _viewSnapshot->rowCount(); }
752 [[nodiscard]] std::optional<std::size_t>
sourceRowOf(std::size_t viewRow)
const {
753 if (viewRow >= _view.size()) {
756 return _view[viewRow];
762 [[nodiscard]] std::optional<std::size_t>
viewRowOf(std::size_t sourceRow)
const {
764 if (sourceRow >= _viewIndex.size() || _viewIndex[sourceRow] == kAbsent) {
767 return _viewIndex[sourceRow];
773 [[nodiscard]]
RowId rowIdAt(std::size_t viewRow)
const {
return _viewSnapshot->rowId(_view.at(viewRow)); }
780 auto const found = _keyIndex.find(key);
781 return found == _keyIndex.end() ? std::nullopt : std::optional{found->second};
789 [[nodiscard]]
Cell cellAt(std::size_t viewRow, std::size_t column)
const {
790 auto const row = _view.
at(viewRow);
791 if (column >= _viewColumns.size()) {
794 return _viewSnapshot->cell(row, column);
799 [[nodiscard]] std::span<std::uint32_t const>
view() const noexcept {
return _view; }
803 [[nodiscard]] std::shared_ptr<RowSnapshot const>
const&
viewSnapshot() const noexcept {
return _viewSnapshot; }
807 [[nodiscard]]
bool pending() const noexcept {
return _pending; }
816 void onPending(std::function<
void(
bool)> handler) { _onPending = std::move(handler); }
844 _releaseOnRepair.insert_or_assign(key, _changeSeq);
846 if (_holds.erase(key) > 0) {
854 std::erase_if(_holds, [&](
RowId const& key) {
return !_editing.contains(key); });
860 [[nodiscard]] std::vector<RowId>
heldRows()
const {
return {_holds.begin(), _holds.end()}; }
863 static constexpr auto kAbsent = std::numeric_limits<std::uint32_t>::max();
865 [[nodiscard]]
static std::vector<std::uint32_t> identity(std::size_t rows) {
866 std::vector<std::uint32_t> out(rows);
868 std::iota(out.begin(), out.end(), 0U);
872 enum class Request : std::uint8_t { Full, Repair };
877 std::uint64_t epoch = 0;
878 std::uint64_t changeSeq = 0;
879 std::uint64_t columnsVersion = 0;
885 bool armSettle =
false;
890 void rowsChanged(RowChange
const& change)
override {
891 switch (change.kind) {
892 case ChangeKind::Updated:
893 for (
auto const row : change.rows) {
894 _notifiedRows.push_back(
static_cast<std::uint32_t
>(row));
897 case ChangeKind::Reset:
898 _notifiedReset =
true;
899 _notifiedStructural =
true;
901 case ChangeKind::Inserted:
902 case ChangeKind::Removed:
904 _notifiedStructural =
true;
909 if (_options.owner ==
nullptr) {
913 if (!_flushScheduled) {
914 _flushScheduled =
true;
915 _options.owner->post(_alive.guard([
this] { flushChanges(); }));
922 void flushChanges() {
924 _flushScheduled =
false;
930 if (
auto const done = schedule(); !done) {
931 reportError(done.error());
943 _absorbedSeq = _changeSeq;
944 _snapshot = _source->snapshot();
945 if (_notifiedReset) {
948 if (_notifiedStructural) {
950 _keys.assign(_columns.size(),
nullptr);
951 _keysSnapshot =
nullptr;
956 _dirty.insert(_dirty.end(), _notifiedRows.begin(), _notifiedRows.end());
958 _notifiedRows.clear();
959 _notifiedStructural =
false;
960 _notifiedReset =
false;
966 void adoptColumns() {
967 std::vector<ColumnInfo>
columns(_source->columns().begin(), _source->columns().end());
976 void fitToColumns() {
977 std::erase_if(_sort, [&](SortKey
const& key) {
return !detail::findColumn(_columns, key.column); });
978 std::erase_if(_filterSpec.columns,
979 [&](
auto const& entry) { return !detail::findColumn(_columns, entry.first).has_value(); });
980 for (
auto& group : _filterSpec.groups) {
981 std::erase_if(group.columns,
982 [&](std::string
const& columnId) { return !detail::findColumn(_columns, columnId); });
984 std::erase_if(_filterSpec.groups, [](GroupFilter
const& group) { return group.columns.empty(); });
985 auto compiled = compile(_filterSpec);
987 _filter = std::make_shared<CompiledFilter const>(std::move(*compiled));
996 [[nodiscard]] std::expected<CompiledFilter, TableError> compile(FilterSpec
const& spec)
const {
999 }
catch (std::exception
const& error) {
1000 return std::unexpected(serviceFailed(error.what()));
1002 return std::unexpected(serviceFailed(
"unknown exception"));
1006 [[nodiscard]]
static TableError serviceFailed(std::string
const& what) {
1007 return TableError{.code = TableErrorCode::ServiceFailed, .column = {}, .message =
"a service failed: " + what};
1013 [[nodiscard]]
bool hasWork()
const {
1014 return !_dirty.empty() || _epoch != _viewEpoch || _sort != _appliedSort || _filter != _appliedFilter ||
1015 _columnsVersion != _viewColumnsVersion;
1022 [[nodiscard]]
bool canRepair()
const {
1023 return !_dirty.empty() && _epoch == _viewEpoch && _sort == _appliedSort && _filter == _appliedFilter &&
1024 _columnsVersion == _viewColumnsVersion;
1028 [[nodiscard]]
bool supersedes(detail::JobInput
const& input, Ticket
const& ticket)
const {
1029 return _sort != input.sort || _filter != input.filter || _epoch != ticket.epoch ||
1030 _columnsVersion != ticket.columnsVersion;
1035 std::expected<void, TableError> schedule() {
1037 if (supersedes(*_runningInput, _runningTicket)) {
1038 _jobStop.request_stop();
1045 auto const kind = canRepair() ? Request::Repair : Request::Full;
1046 if (_options.owner ==
nullptr || _snapshot->rowCount() < _options.smallTable) {
1047 return runNow(kind);
1053 [[nodiscard]] detail::JobInput makeInput(Request kind, Ticket& ticket) {
1054 detail::JobInput input;
1055 input.snapshot = _snapshot;
1056 input.columns = _columns;
1057 input.services = _options.services.forTask();
1059 input.keys.resize(_columns.size());
1060 for (
auto const& key : _sort) {
1061 if (
auto const index = detail::findColumn(_columns, key.column)) {
1062 input.needed.push_back(*index);
1065 if (_filter !=
nullptr) {
1066 for (
auto const column : _filter->columnsUsed()) {
1067 input.needed.push_back(column);
1070 std::ranges::sort(input.needed);
1071 auto const [first, last] = std::ranges::unique(input.needed);
1072 input.needed.erase(first, last);
1074 input.filter = _filter;
1077 if (!_sort.empty() && _sortCache.order !=
nullptr && _sortCache.chain == _sort) {
1078 input.cachedSorted = _sortCache.order;
1080 input.previousView = _view;
1081 input.previousSnapshot = _viewSnapshot;
1082 input.repair = kind == Request::Repair;
1083 input.indicesStable = _epoch == _viewEpoch;
1084 input.dirty = std::move(_dirty);
1086 std::ranges::sort(input.dirty);
1087 auto const [dupFirst, dupLast] = std::ranges::unique(input.dirty);
1088 input.dirty.erase(dupFirst, dupLast);
1090 input.previousTrueView = _trueView;
1092 input.allChanged = _obligeAll;
1094 input.resetThreshold = _options.resetThreshold;
1095 ticket = Ticket{.epoch = _epoch, .changeSeq = _absorbedSeq, .columnsVersion = _columnsVersion};
1100 void giveBack(detail::JobInput
const& input, Ticket
const& ticket) {
1101 if (ticket.epoch == _epoch) {
1102 _dirty.insert(_dirty.end(), input.dirty.begin(), input.dirty.end());
1104 _obligeAll = _obligeAll || input.allChanged;
1109 _sort = _appliedSort;
1110 _filterSpec = _appliedFilterSpec;
1111 _filter = _appliedFilter;
1112 if (_columnsVersion != _viewColumnsVersion) {
1117 std::expected<void, TableError> runNow(Request kind) {
1118 auto const began = std::chrono::steady_clock::now();
1121 detail::ViewJob job{makeInput(kind, ticket)};
1122 static_cast<void>(job.step(kNoDeadline, ::core::async::StopToken{}));
1123 if (job.failure()) {
1124 recordOwnerStep(began);
1125 giveBack(job.input(), ticket);
1127 return std::unexpected(serviceFailed(*job.failure()));
1129 auto const applied = install(job.input(), job.takeResult(), ticket);
1130 recordOwnerStep(began);
1135 void start(Request kind) {
1138 _jobStop = ::core::async::StopSource{};
1139 auto const job = std::make_shared<detail::ViewJob>(makeInput(kind, _runningTicket));
1140 _runningInput = &job->input();
1141 auto*
const owner = _options.owner;
1142 if (_options.worker ==
nullptr) {
1143 owner->
post(_alive.guard([
this, job] { ownerStep(job); }));
1147 if (
auto const progress = _options.services.progress) {
1149 _alive.guard([progress](std::size_t done, std::size_t total) {
progress->progress(done, total); });
1150 job->onPhase([owner, report = std::move(report)](std::size_t done, std::size_t total) {
1151 owner->
post([report, done, total]
mutable { report(done, total); });
1154 auto finished = _alive.guard([
this, job] { finish(job); });
1155 _options.worker->post([job, stop = _jobStop.get_token(), owner, finished = std::move(finished)]
mutable {
1156 static_cast<void>(job->step(kNoDeadline, stop));
1157 owner->post(std::move(finished));
1164 void ownerStep(std::shared_ptr<detail::ViewJob>
const& job) {
1165 auto const began = std::chrono::steady_clock::now();
1166 auto const stop = _jobStop.get_token();
1167 bool const done = job->step(began + _options.frameBudget, stop);
1168 recordOwnerStep(began);
1169 if (
auto const progress = _options.services.progress) {
1170 auto const alive = _alive.token();
1171 auto const [doneUnits, total] = job->progress();
1172 progress->progress(doneUnits, total);
1173 if (alive.expired()) {
1177 if (!done && !stop.stop_requested()) {
1178 _options.owner->post(_alive.guard([
this, job] { ownerStep(job); }));
1184 void finish(std::shared_ptr<detail::ViewJob>
const& job) {
1186 _runningInput =
nullptr;
1187 auto const& input = job->input();
1188 if (!job->done() || supersedes(input, _runningTicket)) {
1190 giveBack(input, _runningTicket);
1191 afterJob(std::nullopt);
1194 if (job->failure()) {
1195 giveBack(input, _runningTicket);
1197 afterJob(serviceFailed(*job->failure()));
1200 auto const began = std::chrono::steady_clock::now();
1201 auto const applied = install(input, job->takeResult(), _runningTicket);
1202 recordOwnerStep(began);
1203 if (!deliver(applied)) {
1206 afterJob(std::nullopt);
1213 void afterJob(std::optional<TableError> error) {
1214 auto const alive = _alive.token();
1215 if (!error && !_running && hasWork()) {
1216 auto const done = schedule();
1217 if (alive.expired()) {
1221 error = done.error();
1224 if (_relayoutPending && !_running) {
1226 if (alive.expired()) {
1232 if (alive.expired()) {
1237 reportError(*error);
1244 [[nodiscard]] Applied install(detail::JobInput
const& input, detail::JobResult result, Ticket
const& ticket) {
1245 _stats.keyBuilds += result.keyBuilds;
1246 _stats.fullSorts += result.fullSort ? 1 : 0;
1247 _stats.subsetSorts += result.subsetSort ? 1 : 0;
1248 _stats.repairs += result.repaired ? 1 : 0;
1249 _keys = std::move(result.keys);
1250 _keysSnapshot = input.snapshot;
1251 if (result.sorted !=
nullptr) {
1252 _sortCache = SortCache{.chain = input.sort, .snapshot = input.snapshot, .order = std::move(result.sorted)};
1253 }
else if (_sortCache.snapshot != input.snapshot) {
1258 MORPH_PLOT(
"table.sourceRows", input.snapshot->rowCount());
1259 MORPH_PLOT(
"table.viewRows", result.view.size());
1261 std::ranges::count_if(_keys, [](
auto const& keys) {
return keys !=
nullptr; }));
1262 bool const columnsChanged = ticket.columnsVersion != _viewColumnsVersion;
1263 if (columnsChanged) {
1264 _viewColumns = input.columns;
1265 _viewColumnsVersion = ticket.columnsVersion;
1267 _viewEpoch = ticket.epoch;
1268 _appliedSort = input.sort;
1269 _appliedFilter = input.filter;
1270 _appliedFilterSpec = _filterSpec;
1271 _trueView = std::move(result.view);
1272 _viewSnapshot = input.snapshot;
1273 _relayoutPending =
false;
1274 _viewIndexValid =
false;
1275 _keyIndexValid =
false;
1276 Applied out{.change = std::move(result.change), .armSettle =
false};
1277 if (_options.reorder == ReorderPolicy::Deferred) {
1280 std::erase_if(_releaseOnRepair, [&](
auto const& entry) {
return entry.second <= _viewChangeSeq; });
1282 holdUpdatedRows(input);
1287 std::erase_if(_releaseOnRepair, [&](
auto const& entry) {
return entry.second <= ticket.changeSeq; });
1289 _viewChangeSeq = ticket.changeSeq;
1290 if (!_holds.empty()) {
1291 auto displayed = holdLayout(input.previousView);
1292 out.change =
diffViews(input.previousView, displayed, input.dirty, input.snapshot->rowCount(),
1293 _options.resetThreshold);
1294 _view = std::move(displayed);
1295 out.armSettle =
true;
1299 if (columnsChanged) {
1301 out.change.ops.assign(1, ViewOp{.kind =
ViewOp::Kind::Reset, .first = 0, .count = 0, .to = 0});
1309 void holdUpdatedRows(detail::JobInput
const& input) {
1310 std::vector<bool> shown(input.snapshot->rowCount(),
false);
1311 for (
auto const row : input.previousView) {
1314 for (
auto const row : input.dirty) {
1315 auto key = input.snapshot->rowId(row);
1316 if (shown[row] && !_releaseOnRepair.contains(key)) {
1317 _holds.insert(std::move(key));
1320 for (
auto const& editing : _editing) {
1321 _holds.insert(editing);
1326 [[nodiscard]] std::vector<std::uint32_t> holdLayout(std::span<std::uint32_t const> displayed)
const {
1327 std::vector<std::pair<std::size_t, std::uint32_t>> held;
1328 std::vector<bool> isHeld(_viewSnapshot->rowCount(),
false);
1329 for (std::size_t i = 0; i < displayed.size(); ++i) {
1330 auto const row = displayed[i];
1331 if (_holds.contains(_viewSnapshot->rowId(row))) {
1332 held.emplace_back(i, row);
1336 std::vector<std::uint32_t> out;
1337 out.reserve(_trueView.size() + held.size());
1338 for (
auto const row : _trueView) {
1343 for (
auto const& [index, row] : held) {
1344 out.insert(out.begin() +
static_cast<std::ptrdiff_t
>(std::min(index, out.size())), row);
1354 _relayoutPending =
true;
1357 _relayoutPending =
false;
1358 auto displayed = _holds.empty() ? _trueView : holdLayout(_view);
1359 auto const change =
diffViews(_view, displayed, {}, _viewSnapshot->rowCount(), _options.resetThreshold);
1360 _view = std::move(displayed);
1361 _viewIndexValid =
false;
1362 _keyIndexValid =
false;
1363 deliver(Applied{.change = change, .armSettle =
false});
1370 bool deliver(Applied
const& applied) {
1371 auto const alive = _alive.token();
1372 if (applied.armSettle && _options.scheduleSettle) {
1373 auto const seq = _changeSeq;
1374 auto const scheduler = _options.scheduleSettle;
1375 scheduler(_options.settle, _alive.guard([
this, seq] {
1376 if (seq == _changeSeq) {
1380 if (alive.expired()) {
1384 if (!applied.change.empty() && _onViewChange) {
1386 auto const handler = _onViewChange;
1387 handler(applied.change);
1389 return !alive.expired();
1392 void setPending(
bool value) {
1393 if (_pending == value) {
1398 auto const handler = _onPending;
1403 void reportError(
TableError const& error)
const {
1405 auto const handler = _onError;
1410 void recordOwnerStep(std::chrono::steady_clock::time_point began) {
1411 auto const micros =
static_cast<std::size_t
>(
1412 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - began).count());
1413 _stats.maxOwnerStepMicros = std::max(_stats.maxOwnerStepMicros, micros);
1414 ++_stats.ownerSteps;
1417 void indexView()
const {
1418 if (_viewIndexValid) {
1421 _viewIndex.assign(_viewSnapshot->rowCount(), kAbsent);
1422 for (std::size_t i = 0; i < _view.size(); ++i) {
1423 _viewIndex[_view[i]] =
static_cast<std::uint32_t
>(i);
1425 _viewIndexValid =
true;
1428 void indexKeys()
const {
1429 if (_keyIndexValid) {
1433 _keyIndex.reserve(_view.size());
1434 for (std::size_t i = 0; i < _view.size(); ++i) {
1435 _keyIndex.emplace(_viewSnapshot->rowId(_view[i]), i);
1437 _keyIndexValid =
true;
1442 std::shared_ptr<RowSnapshot const> snapshot;
1443 std::shared_ptr<std::vector<std::uint32_t>
const> order;
1446 std::shared_ptr<DataSource> _source;
1449 std::vector<ColumnInfo> _columns;
1450 std::shared_ptr<RowSnapshot const> _snapshot;
1453 std::shared_ptr<CompiledFilter const> _filter;
1454 std::uint64_t _columnsVersion = 0;
1455 std::uint64_t _epoch = 0;
1457 std::vector<ColumnInfo> _viewColumns;
1458 std::shared_ptr<RowSnapshot const> _viewSnapshot;
1461 std::shared_ptr<CompiledFilter const> _appliedFilter;
1462 std::uint64_t _viewColumnsVersion = 0;
1463 std::uint64_t _viewEpoch = 0;
1464 std::uint64_t _viewChangeSeq = 0;
1465 std::vector<std::uint32_t> _view;
1466 std::vector<std::uint32_t> _trueView;
1468 std::shared_ptr<RowSnapshot const> _keysSnapshot;
1469 std::vector<std::shared_ptr<KeyColumn const>> _keys;
1470 SortCache _sortCache;
1471 mutable std::vector<std::uint32_t> _viewIndex;
1472 mutable bool _viewIndexValid =
false;
1473 mutable std::unordered_map<RowId, std::size_t> _keyIndex;
1474 mutable bool _keyIndexValid =
false;
1476 std::vector<std::uint32_t> _notifiedRows;
1477 bool _notified =
false;
1478 bool _notifiedStructural =
false;
1479 bool _notifiedReset =
false;
1480 bool _flushScheduled =
false;
1481 std::uint64_t _changeSeq = 0;
1482 std::uint64_t _absorbedSeq = 0;
1483 std::vector<std::uint32_t> _dirty;
1484 bool _obligeAll =
false;
1486 std::unordered_set<RowId> _holds;
1487 std::unordered_set<RowId> _editing;
1488 std::unordered_map<RowId, std::uint64_t> _releaseOnRepair;
1489 bool _relayoutPending =
false;
1491 bool _pending =
false;
1492 bool _running =
false;
1494 Ticket _runningTicket;
1495 ::core::async::StopSource _jobStop;
1496 std::function<void(
ViewChange const&)> _onViewChange;
1497 std::function<void(
bool)> _onPending;
1498 std::function<void(
TableError const&)> _onError;
1500 async::CallbackScope _alive;