@eclipse-glsp/client 2.7.0-next.27 → 2.7.0-next.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/base/action-dispatcher.d.ts +2 -1
- package/lib/base/action-dispatcher.d.ts.map +1 -1
- package/lib/base/action-dispatcher.js +38 -21
- package/lib/base/action-dispatcher.js.map +1 -1
- package/package.json +3 -3
- package/src/base/action-dispatcher.spec.ts +103 -3
- package/src/base/action-dispatcher.ts +42 -22
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { Action, ActionDispatcher, ActionHandler, ActionHandlerRegistry, Deferred, GModelRoot, IActionDispatcher, RequestAction, ResponseAction } from '@eclipse-glsp/sprotty';
|
|
16
|
+
import { Action, ActionDispatcher, ActionHandler, ActionHandlerRegistry, Deferred, GModelRoot, HandleActionResult, IActionDispatcher, RequestAction, ResponseAction } from '@eclipse-glsp/sprotty';
|
|
17
17
|
import { IGModelRootListener } from './editor-context-service';
|
|
18
18
|
import { ModelInitializationConstraint } from './model/model-initialization-constraint';
|
|
19
19
|
export declare class GLSPActionDispatcher extends ActionDispatcher implements IGModelRootListener, IActionDispatcher {
|
|
@@ -51,6 +51,7 @@ export declare class GLSPActionDispatcher extends ActionDispatcher implements IG
|
|
|
51
51
|
protected doHandleAction(action: Action): Promise<void>;
|
|
52
52
|
protected handleActionWithoutHandler(action: Action): Promise<void>;
|
|
53
53
|
protected handleActionWithHandler(action: Action, handlers: ActionHandler[]): Promise<void>;
|
|
54
|
+
protected processHandlerResult(result: HandleActionResult): Promise<unknown> | undefined;
|
|
54
55
|
request<Res extends ResponseAction>(action: RequestAction<Res>): Promise<Res>;
|
|
55
56
|
/**
|
|
56
57
|
* Dispatch a request and waits for a response until the timeout given in `timeoutMs` has
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-dispatcher.d.ts","sourceRoot":"","sources":["../../src/base/action-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;kFAckF;AAClF,OAAO,EACH,MAAM,EACN,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,QAAQ,EAER,UAAU,EACV,iBAAiB,EAGjB,aAAa,EACb,cAAc,EAGjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AAExF,qBACa,oBAAqB,SAAQ,gBAAiB,YAAW,mBAAmB,EAAE,iBAAiB;IACxG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAa;IACrE,SAAS,CAAC,qBAAqB,UAAS;IAGxC,SAAS,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;IAElE,UACmB,qBAAqB,EAAE,qBAAqB,CAAC;IAEhE,gGAAgG;IAEhG,UAAgE,6BAA6B,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACpI,SAAS,CAAC,eAAe,EAAE,MAAM,EAAE,CAAM;IAEzC,SAAS,CAAC,kBAAkB,iBAAwB;IAE3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;cAQpB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAe7C,SAAS,CAAC,wBAAwB,IAAI,IAAI;IAQ1C,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAInC;;;;OAIG;IACH,4BAA4B,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAIxD;;;;;;OAMG;IACH,uBAAuB,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAInD,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI;IAUpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAMnC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAI9C,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"action-dispatcher.d.ts","sourceRoot":"","sources":["../../src/base/action-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;kFAckF;AAClF,OAAO,EACH,MAAM,EACN,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,QAAQ,EAER,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EAGjB,aAAa,EACb,cAAc,EAGjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AAExF,qBACa,oBAAqB,SAAQ,gBAAiB,YAAW,mBAAmB,EAAE,iBAAiB;IACxG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAa;IACrE,SAAS,CAAC,qBAAqB,UAAS;IAGxC,SAAS,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;IAElE,UACmB,qBAAqB,EAAE,qBAAqB,CAAC;IAEhE,gGAAgG;IAEhG,UAAgE,6BAA6B,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACpI,SAAS,CAAC,eAAe,EAAE,MAAM,EAAE,CAAM;IAEzC,SAAS,CAAC,kBAAkB,iBAAwB;IAE3C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;cAQpB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAe7C,SAAS,CAAC,wBAAwB,IAAI,IAAI;IAQ1C,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAInC;;;;OAIG;IACH,4BAA4B,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAIxD;;;;;;OAMG;IACH,uBAAuB,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAInD,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI;IAUpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAMnC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAI9C,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB3E,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAKvC,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAgBzD,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjG,SAAS,CAAC,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS;IAW/E,OAAO,CAAC,GAAG,SAAS,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAOtF;;;;;;;;;OASG;IACH,YAAY,CAAC,GAAG,SAAS,cAAc,EACnC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,EAC1B,SAAS,GAAE,MAA+B,EAC1C,eAAe,UAAQ,GACxB,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;CAuC9B"}
|
|
@@ -108,23 +108,20 @@ let GLSPActionDispatcher = class GLSPActionDispatcher extends sprotty_1.ActionDi
|
|
|
108
108
|
return sprotty_1.ResponseAction.hasValidResponseId(action) ? this.handleResponseAction(action) : this.doHandleAction(action);
|
|
109
109
|
}
|
|
110
110
|
async handleResponseAction(action) {
|
|
111
|
-
// clear any pending timeout
|
|
112
111
|
const timeout = this.timeouts.get(action.responseId);
|
|
113
112
|
if (timeout !== undefined) {
|
|
114
113
|
clearTimeout(timeout);
|
|
115
114
|
this.timeouts.delete(action.responseId);
|
|
116
115
|
}
|
|
117
|
-
// check for matching request
|
|
118
116
|
const request = this.requests.get(action.responseId);
|
|
119
117
|
if (!request) {
|
|
120
|
-
//
|
|
118
|
+
// No pending request: re-dispatch as a normal action.
|
|
121
119
|
this.logger.log(this, 'No matching request for response, dispatch normally', action);
|
|
122
120
|
action.responseId = '';
|
|
123
121
|
return this.handleAction(action);
|
|
124
122
|
}
|
|
125
123
|
this.requests.delete(action.responseId);
|
|
126
124
|
if (sprotty_1.RejectAction.is(action)) {
|
|
127
|
-
// translation reject action to request rejection
|
|
128
125
|
request.reject(new Error(action.message));
|
|
129
126
|
this.logger.warn(this, `Request with id ${action.responseId} failed.`, action.message, action.detail);
|
|
130
127
|
}
|
|
@@ -142,34 +139,45 @@ let GLSPActionDispatcher = class GLSPActionDispatcher extends sprotty_1.ActionDi
|
|
|
142
139
|
}
|
|
143
140
|
this.logger.warn(this, 'Missing handler for action', action);
|
|
144
141
|
const error = new Error(`Missing handler for action '${action.kind}'`);
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
request.reject(error);
|
|
142
|
+
if (sprotty_1.RequestAction.is(action)) {
|
|
143
|
+
const request = this.requests.get(action.requestId);
|
|
144
|
+
if (request !== undefined) {
|
|
145
|
+
this.requests.delete(action.requestId);
|
|
146
|
+
request.reject(error);
|
|
147
|
+
}
|
|
152
148
|
}
|
|
149
|
+
throw error;
|
|
153
150
|
}
|
|
154
151
|
async handleActionWithHandler(action, handlers) {
|
|
155
152
|
this.logger.log(this, 'Handle', action);
|
|
153
|
+
// No `await` inside the loop: invoke handlers in one burst and await their results collectively.
|
|
156
154
|
const handlerResults = [];
|
|
157
155
|
for (const handler of handlers) {
|
|
158
156
|
const maybeResult = handler.handle(action);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
handlerResults.push(this.dispatch(result));
|
|
157
|
+
if (sprotty_1.MaybePromise.isPromise(maybeResult)) {
|
|
158
|
+
handlerResults.push(maybeResult.then(result => this.processHandlerResult(result)));
|
|
162
159
|
}
|
|
163
|
-
else
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
else {
|
|
161
|
+
const resultPromise = this.processHandlerResult(maybeResult);
|
|
162
|
+
if (resultPromise) {
|
|
163
|
+
handlerResults.push(resultPromise);
|
|
164
|
+
}
|
|
166
165
|
}
|
|
167
166
|
}
|
|
168
167
|
await Promise.all(handlerResults);
|
|
169
168
|
}
|
|
169
|
+
processHandlerResult(result) {
|
|
170
|
+
if (sprotty_1.Action.is(result)) {
|
|
171
|
+
return this.dispatch(result);
|
|
172
|
+
}
|
|
173
|
+
if (result !== undefined) {
|
|
174
|
+
this.blockUntil = result.blockUntil;
|
|
175
|
+
return this.commandStack.execute(result);
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
170
179
|
request(action) {
|
|
171
180
|
if (!action.requestId || action.requestId === '') {
|
|
172
|
-
// No request id has been specified. So we use a generated one.
|
|
173
181
|
action.requestId = sprotty_1.RequestAction.generateRequestId();
|
|
174
182
|
}
|
|
175
183
|
return super.request(action);
|
|
@@ -189,7 +197,6 @@ let GLSPActionDispatcher = class GLSPActionDispatcher extends sprotty_1.ActionDi
|
|
|
189
197
|
if (timeoutMs === void 0) { timeoutMs = (_a = action.timeout) !== null && _a !== void 0 ? _a : 2000; }
|
|
190
198
|
if (rejectOnTimeout === void 0) { rejectOnTimeout = false; }
|
|
191
199
|
if (!action.requestId || action.requestId === '') {
|
|
192
|
-
// No request id has been specified. So we use a generated one.
|
|
193
200
|
action.requestId = sprotty_1.RequestAction.generateRequestId();
|
|
194
201
|
}
|
|
195
202
|
// Stamp the effective timeout onto the action so the receiving side
|
|
@@ -199,7 +206,6 @@ let GLSPActionDispatcher = class GLSPActionDispatcher extends sprotty_1.ActionDi
|
|
|
199
206
|
const timeout = setTimeout(() => {
|
|
200
207
|
const deferred = this.requests.get(requestId);
|
|
201
208
|
if (deferred !== undefined) {
|
|
202
|
-
// cleanup
|
|
203
209
|
clearTimeout(timeout);
|
|
204
210
|
this.requests.delete(requestId);
|
|
205
211
|
const notification = 'Request ' + requestId + ' (' + action + ') time out after ' + timeoutMs + 'ms.';
|
|
@@ -213,7 +219,18 @@ let GLSPActionDispatcher = class GLSPActionDispatcher extends sprotty_1.ActionDi
|
|
|
213
219
|
}
|
|
214
220
|
}, timeoutMs);
|
|
215
221
|
this.timeouts.set(requestId, timeout);
|
|
216
|
-
|
|
222
|
+
const result = super.request(action);
|
|
223
|
+
// handleResponseAction only clears the timeout on the response path; clear it here on every
|
|
224
|
+
// other settle path (timeout, rejection, missing handler) so the timer and map entry can't leak.
|
|
225
|
+
const clearRequestTimeout = () => {
|
|
226
|
+
const pending = this.timeouts.get(requestId);
|
|
227
|
+
if (pending !== undefined) {
|
|
228
|
+
clearTimeout(pending);
|
|
229
|
+
this.timeouts.delete(requestId);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
result.then(clearRequestTimeout, clearRequestTimeout);
|
|
233
|
+
return result;
|
|
217
234
|
}
|
|
218
235
|
};
|
|
219
236
|
exports.GLSPActionDispatcher = GLSPActionDispatcher;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-dispatcher.js","sourceRoot":"","sources":["../../src/base/action-dispatcher.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;;;;;;;;;;;;;kFAckF;AAClF,
|
|
1
|
+
{"version":3,"file":"action-dispatcher.js","sourceRoot":"","sources":["../../src/base/action-dispatcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eclipse-glsp/client",
|
|
3
|
-
"version": "2.7.0-next.
|
|
3
|
+
"version": "2.7.0-next.28+4315439",
|
|
4
4
|
"description": "A sprotty-based client for GLSP",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eclipse",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"watch": "tsc -w"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@eclipse-glsp/sprotty": "2.7.0-next.
|
|
49
|
+
"@eclipse-glsp/sprotty": "2.7.0-next.28+4315439",
|
|
50
50
|
"autocompleter": "^9.1.2",
|
|
51
51
|
"file-saver": "^2.0.5",
|
|
52
52
|
"snabbdom": "~3.5.1",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "4315439cbba7eb577881918cb59e2488b31a2a82"
|
|
65
65
|
}
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { ActionHandlerRegistry, IActionHandler, RequestAction, ResponseAction, TYPES } from '@eclipse-glsp/sprotty';
|
|
16
|
+
import { ActionHandlerRegistry, Deferred, IActionHandler, RequestAction, ResponseAction, TYPES } from '@eclipse-glsp/sprotty';
|
|
17
17
|
import { expect } from 'chai';
|
|
18
18
|
import { Container } from 'inversify';
|
|
19
19
|
|
|
20
|
+
/** Yields to the event loop long enough for pending microtasks (and already-queued timers) to run. */
|
|
21
|
+
const flushMicrotasks = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
22
|
+
|
|
20
23
|
import { GLSPActionDispatcher } from './action-dispatcher';
|
|
21
24
|
import { defaultModule } from './default.module';
|
|
22
25
|
import { IDiagramOptions } from './model/diagram-loader';
|
|
@@ -73,6 +76,21 @@ describe('GLSPActionDispatcher', () => {
|
|
|
73
76
|
);
|
|
74
77
|
expect(gotRejected, 'Response promise should be rejected').to.be.true;
|
|
75
78
|
});
|
|
79
|
+
it('should not leak timeout entries once the request settles', async () => {
|
|
80
|
+
const timeouts = actionDispatcher['timeouts'] as Map<string, unknown>;
|
|
81
|
+
|
|
82
|
+
// resolved-within-timeout path
|
|
83
|
+
testHandlerDelay = 5;
|
|
84
|
+
await actionDispatcher.requestUntil({ kind: 'request', requestId: '' }, 150);
|
|
85
|
+
// timed-out path
|
|
86
|
+
testHandlerDelay = 50;
|
|
87
|
+
await actionDispatcher.requestUntil({ kind: 'request', requestId: '' }, 5);
|
|
88
|
+
// missing-handler path: rejects the request, must still clear the timeout
|
|
89
|
+
await actionDispatcher.requestUntil({ kind: 'noHandlerRequest', requestId: '' }, 50).catch(() => undefined);
|
|
90
|
+
await flushMicrotasks();
|
|
91
|
+
|
|
92
|
+
expect(timeouts.size, 'all timeout entries should be cleared after the requests settle').to.equal(0);
|
|
93
|
+
});
|
|
76
94
|
});
|
|
77
95
|
describe('request & re-dispatch', () => {
|
|
78
96
|
it('should be possible to re-dispatch the response of a `request` call', async () => {
|
|
@@ -101,7 +119,9 @@ describe('GLSPActionDispatcher', () => {
|
|
|
101
119
|
expect(handlerExecuted).to.be.true;
|
|
102
120
|
});
|
|
103
121
|
|
|
104
|
-
it('should
|
|
122
|
+
it('should invoke all handlers in one synchronous burst (parallel-start), without serializing on async ones', async () => {
|
|
123
|
+
// asyncHandler1 (registered first) has the longer delay. Serial execution would yield [1, 2];
|
|
124
|
+
// parallel-start lets the shorter handler finish first, yielding [2, 1].
|
|
105
125
|
const executionOrder: number[] = [];
|
|
106
126
|
const asyncHandler1: IActionHandler = {
|
|
107
127
|
handle: async () => {
|
|
@@ -120,7 +140,87 @@ describe('GLSPActionDispatcher', () => {
|
|
|
120
140
|
registry.register('multiAsyncTest', asyncHandler2);
|
|
121
141
|
await actionDispatcher.dispatch({ kind: 'multiAsyncTest' });
|
|
122
142
|
|
|
123
|
-
expect(executionOrder).to.deep.equal([
|
|
143
|
+
expect(executionOrder).to.deep.equal([2, 1]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should not let a slow async handler starve a synchronous sibling registered after it', async () => {
|
|
147
|
+
// Sync handler registered after the gated async one must run during the burst, before the
|
|
148
|
+
// async handler is released.
|
|
149
|
+
const executionOrder: number[] = [];
|
|
150
|
+
const gate = new Deferred<void>();
|
|
151
|
+
const slowAsyncHandler: IActionHandler = {
|
|
152
|
+
handle: async () => {
|
|
153
|
+
await gate.promise;
|
|
154
|
+
executionOrder.push(1);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const syncHandler: IActionHandler = {
|
|
158
|
+
handle: () => {
|
|
159
|
+
executionOrder.push(2);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
registry.register('starveTest', slowAsyncHandler);
|
|
164
|
+
registry.register('starveTest', syncHandler);
|
|
165
|
+
const dispatched = actionDispatcher.dispatch({ kind: 'starveTest' });
|
|
166
|
+
|
|
167
|
+
// Let the invocation burst run; the async handler is still gated.
|
|
168
|
+
await flushMicrotasks();
|
|
169
|
+
expect(executionOrder, 'sync sibling must run before the gated async handler resolves').to.deep.equal([2]);
|
|
170
|
+
|
|
171
|
+
gate.resolve();
|
|
172
|
+
await dispatched;
|
|
173
|
+
expect(executionOrder).to.deep.equal([2, 1]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should not let a never-resolving async handler starve a synchronous sibling', async () => {
|
|
177
|
+
// A handler whose promise never resolves must not prevent other handlers from being invoked.
|
|
178
|
+
const executionOrder: number[] = [];
|
|
179
|
+
const stuckHandler: IActionHandler = {
|
|
180
|
+
handle: () => new Promise<void>(() => undefined) // never resolves
|
|
181
|
+
};
|
|
182
|
+
const syncHandler: IActionHandler = {
|
|
183
|
+
handle: () => {
|
|
184
|
+
executionOrder.push(1);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
registry.register('stuckTest', stuckHandler);
|
|
189
|
+
registry.register('stuckTest', syncHandler);
|
|
190
|
+
// Intentionally not awaited: the dispatch promise never resolves because of the stuck handler.
|
|
191
|
+
actionDispatcher.dispatch({ kind: 'stuckTest' });
|
|
192
|
+
|
|
193
|
+
await flushMicrotasks();
|
|
194
|
+
expect(executionOrder).to.deep.equal([1]);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should not deadlock when an async handler awaits a nested dispatch while a sibling is registered after it', async () => {
|
|
198
|
+
// A serial loop would block the sibling behind the async handler that awaits a nested dispatch,
|
|
199
|
+
// risking deadlock. Parallel-start invokes the sibling in the burst, so nothing waits its turn.
|
|
200
|
+
// Order: 'B' (burst) -> 'sub' (nested) -> 'A' (async tail).
|
|
201
|
+
const executionOrder: string[] = [];
|
|
202
|
+
registry.register('nestedSub', {
|
|
203
|
+
handle: () => {
|
|
204
|
+
executionOrder.push('sub');
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
const nestedDispatchHandler: IActionHandler = {
|
|
208
|
+
handle: async () => {
|
|
209
|
+
await actionDispatcher.dispatch({ kind: 'nestedSub' });
|
|
210
|
+
executionOrder.push('A');
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const siblingHandler: IActionHandler = {
|
|
214
|
+
handle: () => {
|
|
215
|
+
executionOrder.push('B');
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
registry.register('nestedTest', nestedDispatchHandler);
|
|
220
|
+
registry.register('nestedTest', siblingHandler);
|
|
221
|
+
await actionDispatcher.dispatch({ kind: 'nestedTest' });
|
|
222
|
+
|
|
223
|
+
expect(executionOrder).to.deep.equal(['B', 'sub', 'A']);
|
|
124
224
|
});
|
|
125
225
|
|
|
126
226
|
it('should handle mixed sync and async handlers correctly', async () => {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
Deferred,
|
|
22
22
|
EMPTY_ROOT,
|
|
23
23
|
GModelRoot,
|
|
24
|
+
HandleActionResult,
|
|
24
25
|
IActionDispatcher,
|
|
25
26
|
MaybePromise,
|
|
26
27
|
RejectAction,
|
|
@@ -133,17 +134,15 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
protected async handleResponseAction(action: ResponseAction): Promise<void> {
|
|
136
|
-
// clear any pending timeout
|
|
137
137
|
const timeout = this.timeouts.get(action.responseId);
|
|
138
138
|
if (timeout !== undefined) {
|
|
139
139
|
clearTimeout(timeout);
|
|
140
140
|
this.timeouts.delete(action.responseId);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
// check for matching request
|
|
144
143
|
const request = this.requests.get(action.responseId);
|
|
145
144
|
if (!request) {
|
|
146
|
-
//
|
|
145
|
+
// No pending request: re-dispatch as a normal action.
|
|
147
146
|
this.logger.log(this, 'No matching request for response, dispatch normally', action);
|
|
148
147
|
action.responseId = '';
|
|
149
148
|
return this.handleAction(action);
|
|
@@ -151,7 +150,6 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
151
150
|
|
|
152
151
|
this.requests.delete(action.responseId);
|
|
153
152
|
if (RejectAction.is(action)) {
|
|
154
|
-
// translation reject action to request rejection
|
|
155
153
|
request.reject(new Error(action.message));
|
|
156
154
|
this.logger.warn(this, `Request with id ${action.responseId} failed.`, action.message, action.detail);
|
|
157
155
|
} else {
|
|
@@ -170,35 +168,47 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
170
168
|
}
|
|
171
169
|
this.logger.warn(this, 'Missing handler for action', action);
|
|
172
170
|
const error = new Error(`Missing handler for action '${action.kind}'`);
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
request.reject(error);
|
|
171
|
+
if (RequestAction.is(action)) {
|
|
172
|
+
const request = this.requests.get(action.requestId);
|
|
173
|
+
if (request !== undefined) {
|
|
174
|
+
this.requests.delete(action.requestId);
|
|
175
|
+
request.reject(error);
|
|
176
|
+
}
|
|
180
177
|
}
|
|
178
|
+
throw error;
|
|
181
179
|
}
|
|
182
180
|
|
|
183
181
|
protected async handleActionWithHandler(action: Action, handlers: ActionHandler[]): Promise<void> {
|
|
184
182
|
this.logger.log(this, 'Handle', action);
|
|
185
|
-
|
|
183
|
+
// No `await` inside the loop: invoke handlers in one burst and await their results collectively.
|
|
184
|
+
const handlerResults: PromiseLike<unknown>[] = [];
|
|
186
185
|
for (const handler of handlers) {
|
|
187
186
|
const maybeResult = handler.handle(action);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
if (MaybePromise.isPromise(maybeResult)) {
|
|
188
|
+
handlerResults.push(maybeResult.then(result => this.processHandlerResult(result)));
|
|
189
|
+
} else {
|
|
190
|
+
const resultPromise = this.processHandlerResult(maybeResult);
|
|
191
|
+
if (resultPromise) {
|
|
192
|
+
handlerResults.push(resultPromise);
|
|
193
|
+
}
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
await Promise.all(handlerResults);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
protected processHandlerResult(result: HandleActionResult): Promise<unknown> | undefined {
|
|
200
|
+
if (Action.is(result)) {
|
|
201
|
+
return this.dispatch(result);
|
|
202
|
+
}
|
|
203
|
+
if (result !== undefined) {
|
|
204
|
+
this.blockUntil = result.blockUntil;
|
|
205
|
+
return this.commandStack.execute(result);
|
|
206
|
+
}
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
|
|
199
210
|
override request<Res extends ResponseAction>(action: RequestAction<Res>): Promise<Res> {
|
|
200
211
|
if (!action.requestId || action.requestId === '') {
|
|
201
|
-
// No request id has been specified. So we use a generated one.
|
|
202
212
|
action.requestId = RequestAction.generateRequestId();
|
|
203
213
|
}
|
|
204
214
|
return super.request(action);
|
|
@@ -220,7 +230,6 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
220
230
|
rejectOnTimeout = false
|
|
221
231
|
): Promise<Res | undefined> {
|
|
222
232
|
if (!action.requestId || action.requestId === '') {
|
|
223
|
-
// No request id has been specified. So we use a generated one.
|
|
224
233
|
action.requestId = RequestAction.generateRequestId();
|
|
225
234
|
}
|
|
226
235
|
// Stamp the effective timeout onto the action so the receiving side
|
|
@@ -231,7 +240,6 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
231
240
|
const timeout = setTimeout(() => {
|
|
232
241
|
const deferred = this.requests.get(requestId);
|
|
233
242
|
if (deferred !== undefined) {
|
|
234
|
-
// cleanup
|
|
235
243
|
clearTimeout(timeout);
|
|
236
244
|
this.requests.delete(requestId);
|
|
237
245
|
|
|
@@ -245,6 +253,18 @@ export class GLSPActionDispatcher extends ActionDispatcher implements IGModelRoo
|
|
|
245
253
|
}
|
|
246
254
|
}, timeoutMs);
|
|
247
255
|
this.timeouts.set(requestId, timeout);
|
|
248
|
-
|
|
256
|
+
|
|
257
|
+
const result = super.request<Res>(action);
|
|
258
|
+
// handleResponseAction only clears the timeout on the response path; clear it here on every
|
|
259
|
+
// other settle path (timeout, rejection, missing handler) so the timer and map entry can't leak.
|
|
260
|
+
const clearRequestTimeout = (): void => {
|
|
261
|
+
const pending = this.timeouts.get(requestId);
|
|
262
|
+
if (pending !== undefined) {
|
|
263
|
+
clearTimeout(pending);
|
|
264
|
+
this.timeouts.delete(requestId);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
result.then(clearRequestTimeout, clearRequestTimeout);
|
|
268
|
+
return result;
|
|
249
269
|
}
|
|
250
270
|
}
|