diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java index ba5686df6d1..1829a1ee7c6 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java @@ -10,6 +10,7 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; import elemental2.core.Uint8Array; +import elemental2.dom.AbortController; import elemental2.dom.DomGlobal; import elemental2.promise.IThenable; import elemental2.promise.Promise; @@ -18,6 +19,7 @@ import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.hierarchicaltable_pb.HierarchicalTableSourceExportRequest; import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.hierarchicaltable_pb.HierarchicalTableViewKeyTableDescriptor; import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.hierarchicaltable_pb.HierarchicalTableViewRequest; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.hierarchicaltable_pb_service.UnaryResponse; import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.table_pb.Condition; import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.table_pb.ExportedTableCreationResponse; import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.table_pb.SortDescriptor; @@ -150,7 +152,7 @@ private enum RebuildStep { private Object[][] keyTableData; private Promise keyTable; - private TicketAndPromise viewTicket; + private TicketAndPromise viewTicket; private Promise stream; // the "next" set of filters/sorts that we'll use. these either are "==" to the above fields, or are scheduled @@ -348,30 +350,64 @@ private Promise makeKeyTable() { return keyTable; } - private TicketAndPromise makeView(TicketAndPromise prevTicket) { + private TicketAndPromise makeView(TicketAndPromise prevTicket) { if (viewTicket != null) { return viewTicket; } Ticket ticket = connection.getConfig().newTicket(); Promise keyTable = makeKeyTable(); + AbortController controller = new AbortController(); + viewTicket = new TicketAndPromise<>(ticket, Callbacks.grpcUnaryPromise(c -> { HierarchicalTableViewRequest viewRequest = new HierarchicalTableViewRequest(); viewRequest.setHierarchicalTableId(prevTicket.ticket()); viewRequest.setResultViewId(ticket); keyTable.then(t -> { + if (controller.signal.aborted) { + return Promise.resolve(controller.signal.reason); + } if (keyTableData[0].length > 0) { HierarchicalTableViewKeyTableDescriptor expansions = new HierarchicalTableViewKeyTableDescriptor(); expansions.setKeyTableId(t.getHandle().makeTicket()); expansions.setKeyTableActionColumn(actionCol.getName()); viewRequest.setExpansions(expansions); } - connection.hierarchicalTableServiceClient().view(viewRequest, connection.metadata(), c::apply); + UnaryResponse viewCreationCall = + connection.hierarchicalTableServiceClient().view(viewRequest, connection.metadata(), c::apply); + controller.signal.addEventListener("abort", e -> viewCreationCall.cancel()); return null; }, error -> { c.apply(error, null); return null; }); - }), connection); + }).then(result -> { + if (controller.signal.aborted) { + return Promise.reject(controller.signal.reason); + } + ClientTableState state = new ClientTableState(connection, + new TableTicket(viewTicket.ticket().getTicket_asU8()), (callback, newState, metadata) -> { + callback.apply("fail, trees dont reconnect like this", null); + }, ""); + state.retain(JsTreeTable.this); + ExportedTableCreationResponse def = new ExportedTableCreationResponse(); + HierarchicalTableDescriptor treeDescriptor = + HierarchicalTableDescriptor.deserializeBinary(widget.getDataAsU8()); + def.setSchemaHeader(treeDescriptor.getSnapshotSchema_asU8()); + def.setResultId(new TableReference()); + def.getResultId().setTicket(viewTicket.ticket()); + state.applyTableCreationResponse(def); + return Promise.resolve(state); + }), connection) { + @Override + public void release() { + super.release(); + controller.abort(); + then(state -> { + state.unretain(JsTreeTable.this); + return null; + }); + } + }; return viewTicket; } @@ -622,16 +658,16 @@ private void replaceSubscription(RebuildStep step) { Promise stream = Promise.resolve(defer()) .then(ignore -> { makeKeyTable(); - TicketAndPromise filter = prepareFilter(); - TicketAndPromise sort = prepareSort(filter); - TicketAndPromise view = makeView(sort); + TicketAndPromise filter = prepareFilter(); + TicketAndPromise sort = prepareSort(filter); + TicketAndPromise view = makeView(sort); return Promise.all( keyTable, filter.promise(), - sort.promise(), - view.promise()); + sort.promise()) + .then(others -> view.promise()); }) - .then(results -> { + .then(state -> { BitSet columnsBitset = makeColumnSubscriptionBitset(); RangeSet range = RangeSet.ofRange((long) (double) firstRow, (long) (double) lastRow); @@ -644,18 +680,6 @@ private void replaceSubscription(RebuildStep step) { range, alwaysFireEvent); - ClientTableState state = new ClientTableState(connection, - new TableTicket(viewTicket.ticket().getTicket_asU8()), (callback, newState, metadata) -> { - callback.apply("fail, trees dont reconnect like this", null); - }, ""); - ExportedTableCreationResponse def = new ExportedTableCreationResponse(); - HierarchicalTableDescriptor treeDescriptor = - HierarchicalTableDescriptor.deserializeBinary(widget.getDataAsU8()); - def.setSchemaHeader(treeDescriptor.getSnapshotSchema_asU8()); - def.setResultId(new TableReference()); - def.getResultId().setTicket(viewTicket.ticket()); - state.applyTableCreationResponse(def); - TreeSubscription subscription = new TreeSubscription(state, connection); subscription.addEventListener(TreeSubscription.EVENT_UPDATED,