Skip to content

Commit 0425c61

Browse files
authored
fix for issue #1178 (BulletAppState violates AppState contract) (#1187)
* fix for issue #1178 (BulletAppState violates AppState contract) * BulletAppState: make isRunning volatile (potential multithreaded access)
1 parent dcb6697 commit 0425c61

File tree

1 file changed

+23
-73
lines changed

1 file changed

+23
-73
lines changed

jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java

Lines changed: 23 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2018 jMonkeyEngine
2+
* Copyright (c) 2009-2019 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
3232
package com.jme3.bullet;
3333

3434
import com.jme3.app.Application;
35-
import com.jme3.app.state.AppState;
35+
import com.jme3.app.state.AbstractAppState;
3636
import com.jme3.app.state.AppStateManager;
3737
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
3838
import com.jme3.bullet.debug.BulletDebugAppState;
@@ -49,27 +49,16 @@
4949
*
5050
* @author normenhansen
5151
*/
52-
public class BulletAppState implements AppState, PhysicsTickListener {
53-
54-
// FIXME: the bullet app state doesn't follow the proper AppState
55-
// contract as it messes with its initialized state independently
56-
// of when initialize()/cleanup() is actually called. This means
57-
// that it's quite likely that the state manager will think the
58-
// app state is initialized when it, itself, doesn't. This is
59-
// a good example of why extending the abstract app state classes
60-
// is better than implementing app state directly. If it wants
61-
// to support a separate stated/not-started concept then that's
62-
// separate from initialized/not-initialized but way more refactoring
63-
// than I want to think about today. -pspeed:2019-09-15
52+
public class BulletAppState
53+
extends AbstractAppState
54+
implements PhysicsTickListener {
6455

6556
/**
6657
* true if-and-only-if the physics simulation is running (started but not
6758
* yet stopped)
6859
*/
69-
protected boolean initialized = false;
60+
protected volatile boolean isRunning = false;
7061
protected Application app;
71-
private String id;
72-
7362
/**
7463
* manager that manages this state, set during attach
7564
*/
@@ -245,7 +234,7 @@ public PhysicsSpace getPhysicsSpace() {
245234
* sooner, invoke this method.
246235
*/
247236
public void startPhysics() {
248-
if (initialized) {
237+
if (isRunning) {
249238
return;
250239
}
251240

@@ -265,14 +254,14 @@ public void startPhysics() {
265254
throw new IllegalStateException(threadingType.toString());
266255
}
267256

268-
initialized = true;
257+
isRunning = true;
269258
}
270259

271260
/**
272261
* Stop physics after this state is detached.
273262
*/
274263
public void stopPhysics() {
275-
if(!initialized){
264+
if (!isRunning) {
276265
return;
277266
}
278267
if (executor != null) {
@@ -281,7 +270,7 @@ public void stopPhysics() {
281270
}
282271
pSpace.removeTickListener(this);
283272
pSpace.destroy();
284-
initialized = false;
273+
isRunning = false;
285274
}
286275

287276
/**
@@ -291,54 +280,14 @@ public void stopPhysics() {
291280
* @param stateManager the manager for this state (not null)
292281
* @param app the application which owns this state (not null)
293282
*/
283+
@Override
294284
public void initialize(AppStateManager stateManager, Application app) {
285+
super.initialize(stateManager, app);
295286
this.app = app;
296287
this.stateManager = stateManager;
297288
startPhysics();
298289
}
299290

300-
/**
301-
* Test whether the physics simulation is running (started but not yet
302-
* stopped).
303-
*
304-
* @return true if running, otherwise false
305-
*/
306-
public boolean isInitialized() {
307-
return initialized;
308-
}
309-
310-
/**
311-
* Sets the unique ID of this app state. Note: that setting
312-
* this while an app state is attached to the state manager will
313-
* have no effect on ID-based lookups.
314-
*/
315-
protected void setId( String id ) {
316-
this.id = id;
317-
}
318-
319-
@Override
320-
public String getId() {
321-
return id;
322-
}
323-
324-
/**
325-
* Enable or disable this state.
326-
*
327-
* @param enabled true → enable, false → disable
328-
*/
329-
public void setEnabled(boolean enabled) {
330-
this.active = enabled;
331-
}
332-
333-
/**
334-
* Test whether this state is enabled.
335-
*
336-
* @return true if enabled, otherwise false
337-
*/
338-
public boolean isEnabled() {
339-
return active;
340-
}
341-
342291
/**
343292
* Alter whether debug visualization is enabled.
344293
*
@@ -364,8 +313,10 @@ public boolean isDebugEnabled() {
364313
*
365314
* @param stateManager (not null)
366315
*/
316+
@Override
367317
public void stateAttached(AppStateManager stateManager) {
368-
if (!initialized) {
318+
super.stateAttached(stateManager);
319+
if (!isRunning) {
369320
startPhysics();
370321
}
371322
if (threadingType == ThreadingType.PARALLEL) {
@@ -377,23 +328,16 @@ public void stateAttached(AppStateManager stateManager) {
377328
}
378329
}
379330

380-
/**
381-
* Transition this state from running to terminating. Should be invoked only
382-
* by a subclass or by the AppStateManager.
383-
*
384-
* @param stateManager (not null)
385-
*/
386-
public void stateDetached(AppStateManager stateManager) {
387-
}
388-
389331
/**
390332
* Update this state prior to rendering. Should be invoked only by a
391333
* subclass or by the AppStateManager. Invoked once per frame, provided the
392334
* state is attached and enabled.
393335
*
394336
* @param tpf the time interval between frames (in seconds, ≥0)
395337
*/
338+
@Override
396339
public void update(float tpf) {
340+
super.update(tpf);
397341
if (debugEnabled && debugAppState == null && pSpace != null) {
398342
debugAppState = new BulletDebugAppState(pSpace);
399343
stateManager.attach(debugAppState);
@@ -415,7 +359,9 @@ public void update(float tpf) {
415359
*
416360
* @param rm the render manager (not null)
417361
*/
362+
@Override
418363
public void render(RenderManager rm) {
364+
super.render(rm);
419365
if (!active) {
420366
return;
421367
}
@@ -432,7 +378,9 @@ public void render(RenderManager rm) {
432378
* invoked only by a subclass or by the AppStateManager. Invoked once per
433379
* frame, provided the state is attached and enabled.
434380
*/
381+
@Override
435382
public void postRender() {
383+
super.postRender();
436384
if (physicsFuture != null) {
437385
try {
438386
physicsFuture.get();
@@ -451,12 +399,14 @@ public void postRender() {
451399
* {@link #initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)}
452400
* is invoked.
453401
*/
402+
@Override
454403
public void cleanup() {
455404
if (debugAppState != null) {
456405
stateManager.detach(debugAppState);
457406
debugAppState = null;
458407
}
459408
stopPhysics();
409+
super.cleanup();
460410
}
461411

462412
/**

0 commit comments

Comments
 (0)