Skip to content

Commit e017d13

Browse files
#17 Improve Thread management (#18)
- Handle model updates via the GLSP ActionDispatcher, to make sure model updates happen in the correct thread - Wait for undo/redo requests to complete before accepting the next action(s) in the GLSP ActionDispatcher
1 parent 8f3f3d1 commit e017d13

7 files changed

Lines changed: 148 additions & 15 deletions

File tree

plugins/org.eclipse.emfcloud.modelserver.glsp.integration/src/org/eclipse/emfcloud/modelserver/glsp/EMSGLSPModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.eclipse.emfcloud.modelserver.glsp.actions.handlers.EMSRedoActionHandler;
1515
import org.eclipse.emfcloud.modelserver.glsp.actions.handlers.EMSSaveModelActionHandler;
1616
import org.eclipse.emfcloud.modelserver.glsp.actions.handlers.EMSUndoActionHandler;
17+
import org.eclipse.emfcloud.modelserver.glsp.actions.handlers.EMSRefreshModelActionHandler;
1718
import org.eclipse.emfcloud.modelserver.glsp.layout.EMSLayoutEngine;
1819
import org.eclipse.glsp.server.actions.ActionHandler;
1920
import org.eclipse.glsp.server.actions.SaveModelActionHandler;
@@ -33,6 +34,7 @@ protected void configureActionHandlers(final MultiBinding<ActionHandler> binding
3334
bindings.remove(UndoRedoActionHandler.class);
3435
bindings.add(EMSUndoActionHandler.class);
3536
bindings.add(EMSRedoActionHandler.class);
37+
bindings.add(EMSRefreshModelActionHandler.class);
3638
}
3739

3840
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/********************************************************************************
2+
* Copyright (c) 2021 EclipseSource and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0, or the MIT License which is
7+
* available at https://opensource.org/licenses/MIT.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR MIT
10+
********************************************************************************/
11+
package org.eclipse.emfcloud.modelserver.glsp.actions;
12+
13+
import org.eclipse.glsp.server.actions.Action;
14+
15+
/**
16+
* An action to trigger the refresh of the model from the ModelServer,
17+
* after we receive an update (incremental or full).
18+
*/
19+
public class EMSRefreshModelAction extends Action {
20+
21+
public static final String KIND = "ems.refresh";
22+
23+
public EMSRefreshModelAction() {
24+
super(KIND);
25+
}
26+
27+
}

plugins/org.eclipse.emfcloud.modelserver.glsp.integration/src/org/eclipse/emfcloud/modelserver/glsp/actions/handlers/EMSRedoActionHandler.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
package org.eclipse.emfcloud.modelserver.glsp.actions.handlers;
1212

1313
import java.util.List;
14+
import java.util.concurrent.CompletableFuture;
15+
import java.util.concurrent.ExecutionException;
16+
import java.util.concurrent.TimeUnit;
17+
import java.util.concurrent.TimeoutException;
1418

1519
import org.apache.log4j.Logger;
1620
import org.eclipse.emfcloud.modelserver.glsp.EMSModelServerAccess;
@@ -26,12 +30,42 @@ public class EMSRedoActionHandler extends EMSBasicActionHandler<RedoAction, EMSM
2630
public List<Action> executeAction(final RedoAction action, final EMSModelState modelState,
2731
final EMSModelServerAccess modelServerAccess) {
2832

29-
modelServerAccess.redo().thenAccept(response -> {
30-
if (!response.body()) {
31-
LOGGER.warn("Cannot redo.");
33+
CompletableFuture<Void> result = modelServerAccess.redo().thenAccept(response -> {
34+
int status = response.getStatusCode();
35+
switch (status) {
36+
case 200: // OK
37+
case 202: // Not redoable
38+
return;
39+
default:
40+
// Typically error 500: ModelServer error
41+
LOGGER.error("Invalid redo response: " + response.getStatusCode());
42+
return;
3243
}
3344
});
3445

46+
// Make sure we wait for the redo result before proceeding with more actions
47+
// Don't block forever, though.
48+
// Note: this is especially important as the moment, because the ModelServer itself
49+
// isn't thread safe. If we send multiple parallel redo requests, it may try to handle
50+
// more than one at the same time, causing crashes. But even with a thread-safe model server,
51+
// it's a good idea to wait a bit to make sure we're in a consistent state before handling
52+
// more actions.
53+
int maxTries = 5;
54+
for (int i = 0; i < maxTries; i++) {
55+
try {
56+
result.get(1, TimeUnit.SECONDS);
57+
} catch (TimeoutException e) {
58+
LOGGER.warn("Redo action didn't complete in " + (i + 1) + "s");
59+
} catch (InterruptedException e) {
60+
LOGGER.error("Interrupted", e);
61+
Thread.currentThread().interrupt();
62+
break;
63+
} catch (ExecutionException e) {
64+
LOGGER.error("Failed to redo", e);
65+
break;
66+
}
67+
}
68+
3569
return none();
3670
}
3771

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/********************************************************************************
2+
* Copyright (c) 2021 EclipseSource and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0, or the MIT License which is
7+
* available at https://opensource.org/licenses/MIT.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR MIT
10+
********************************************************************************/
11+
package org.eclipse.emfcloud.modelserver.glsp.actions.handlers;
12+
13+
import java.util.List;
14+
15+
import org.eclipse.emfcloud.modelserver.glsp.actions.EMSRefreshModelAction;
16+
import org.eclipse.emfcloud.modelserver.glsp.model.EMSModelState;
17+
import org.eclipse.glsp.server.actions.AbstractActionHandler;
18+
import org.eclipse.glsp.server.actions.Action;
19+
import org.eclipse.glsp.server.features.core.model.ModelSubmissionHandler;
20+
21+
import com.google.inject.Inject;
22+
23+
/**
24+
* Handles model updates with an ActionHandler, to make sure we're in a thread-safe context.
25+
*/
26+
public class EMSRefreshModelActionHandler extends AbstractActionHandler<EMSRefreshModelAction> {
27+
28+
@Inject
29+
protected ModelSubmissionHandler submissionHandler;
30+
31+
@Inject
32+
protected EMSModelState modelState;
33+
34+
@Override
35+
protected List<Action> executeAction(final EMSRefreshModelAction actualAction) {
36+
// reload models
37+
modelState.loadSourceModels();
38+
// refresh GModelRoot
39+
return submissionHandler.submitModel();
40+
}
41+
42+
}

plugins/org.eclipse.emfcloud.modelserver.glsp.integration/src/org/eclipse/emfcloud/modelserver/glsp/actions/handlers/EMSUndoActionHandler.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
package org.eclipse.emfcloud.modelserver.glsp.actions.handlers;
1212

1313
import java.util.List;
14+
import java.util.concurrent.CompletableFuture;
15+
import java.util.concurrent.ExecutionException;
16+
import java.util.concurrent.TimeUnit;
17+
import java.util.concurrent.TimeoutException;
1418

1519
import org.apache.log4j.Logger;
1620
import org.eclipse.emfcloud.modelserver.glsp.EMSModelServerAccess;
@@ -27,11 +31,40 @@ public class EMSUndoActionHandler
2731
public List<Action> executeAction(final UndoAction action, final EMSModelState modelState,
2832
final EMSModelServerAccess modelServerAccess) {
2933

30-
modelServerAccess.undo().thenAccept(response -> {
31-
if (!response.body()) {
32-
LOGGER.warn("Cannot undo.");
34+
CompletableFuture<Void> result = modelServerAccess.undo().thenAccept(response -> {
35+
int status = response.getStatusCode();
36+
switch (status) {
37+
case 200: // OK
38+
case 202: // Not undoable
39+
return;
40+
default:
41+
// Typically error 500: ModelServer error
42+
LOGGER.error("Invalid undo response: " + response.getStatusCode());
43+
return;
3344
}
3445
});
46+
// Make sure we wait for the undo result before proceeding with more actions
47+
// Don't block forever, though.
48+
// Note: this is especially important as the moment, because the ModelServer itself
49+
// isn't thread safe. If we send multiple parallel undo requests, it may try to handle
50+
// more than one at the same time, causing crashes. But even with a thread-safe model server,
51+
// it's a good idea to wait a bit to make sure we're in a consistent state before handling
52+
// more actions.
53+
int maxTries = 5;
54+
for (int i = 0; i < maxTries; i++) {
55+
try {
56+
result.get(1, TimeUnit.SECONDS);
57+
} catch (TimeoutException e) {
58+
LOGGER.warn("Undo action didn't complete in " + (i + 1) + "s");
59+
} catch (InterruptedException e) {
60+
LOGGER.error("Interrupted", e);
61+
Thread.currentThread().interrupt();
62+
break;
63+
} catch (ExecutionException e) {
64+
LOGGER.error("Failed to undo", e);
65+
break;
66+
}
67+
}
3568

3669
return none();
3770
}

plugins/org.eclipse.emfcloud.modelserver.glsp.integration/src/org/eclipse/emfcloud/modelserver/glsp/model/EMSSubscriptionListener.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
import org.eclipse.emf.ecore.EObject;
1717
import org.eclipse.emfcloud.modelserver.client.XmiToEObjectSubscriptionListener;
1818
import org.eclipse.emfcloud.modelserver.command.CCommandExecutionResult;
19+
import org.eclipse.emfcloud.modelserver.glsp.actions.EMSRefreshModelAction;
1920
import org.eclipse.glsp.server.actions.ActionDispatcher;
2021
import org.eclipse.glsp.server.actions.SetDirtyStateAction;
2122
import org.eclipse.glsp.server.features.core.model.ModelSubmissionHandler;
22-
import org.eclipse.glsp.server.features.core.model.RequestBoundsAction;
2323

2424
public class EMSSubscriptionListener extends XmiToEObjectSubscriptionListener {
2525

26-
private static Logger LOGGER = Logger.getLogger(EMSSubscriptionListener.class.getSimpleName());
26+
private static final Logger LOGGER = Logger.getLogger(EMSSubscriptionListener.class.getSimpleName());
2727

2828
protected final ActionDispatcher actionDispatcher;
2929
protected final EMSModelState modelState;
@@ -49,12 +49,7 @@ public void onFullUpdate(final EObject fullUpdate) {
4949
}
5050

5151
protected void refresh() {
52-
// reload models
53-
modelState.loadSourceModels();
54-
// refresh GModelRoot
55-
submissionHandler.submitModel();
56-
// requestboundsaction in submissionhandler not enough?
57-
actionDispatcher.dispatch(new RequestBoundsAction(modelState.getRoot()));
52+
actionDispatcher.dispatch(new EMSRefreshModelAction());
5853
}
5954

6055
@Override

releng/org.eclipse.emfcloud.modelserver.glsp.releng.target/targetdefinition.target

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
22
<?pde?>
33
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
4-
<target name="EMF.cloud Model Server GLSP Integration Targetplatform" sequenceNumber="1637147013">
4+
<target name="EMF.cloud Model Server GLSP Integration Targetplatform" sequenceNumber="1637573334">
55
<locations>
66
<location includeMode="planner" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="false" type="InstallableUnit">
77
<unit id="org.eclipse.glsp.feature.feature.group" version="0.9.0.202111041528"/>

0 commit comments

Comments
 (0)