From eabc47a6687829f175a9212d7a18aeba64c3c835 Mon Sep 17 00:00:00 2001
From: Michael Zangl <michael.zangl@student.kit.edu>
Date: Thu, 6 Aug 2015 14:26:36 +0200
Subject: [PATCH 3/3] Changed locks to use java synchronized.
---
src/org/openstreetmap/josm/gui/MapView.java | 182 ++++++++++------------------
1 file changed, 66 insertions(+), 116 deletions(-)
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index c1ec650..7f114b6 100644
a
|
b
|
import java.util.List;
|
31 | 31 | import java.util.ListIterator; |
32 | 32 | import java.util.Set; |
33 | 33 | import java.util.concurrent.CopyOnWriteArrayList; |
34 | | import java.util.concurrent.locks.ReentrantReadWriteLock; |
35 | 34 | |
36 | 35 | import javax.swing.AbstractButton; |
37 | 36 | import javax.swing.ActionMap; |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
229 | 228 | } |
230 | 229 | |
231 | 230 | /** |
232 | | * This is a simple invariant check that tests if the {@link #layerLock} is not write locked. |
| 231 | * This is a simple invariant check that tests if the {@link #layersChangingMutex} is held and the {@link #layersMutex} not. |
233 | 232 | * This should be the case whenever a layer listener is invoked. |
234 | 233 | */ |
235 | 234 | private void checkLayerLockNotHeld() { |
236 | | if (layerLock.isWriteLockedByCurrentThread()) { |
237 | | Main.warn("layerLock is write-held while a listener was called."); |
| 235 | if (Thread.holdsLock(layersMutex)) { |
| 236 | Main.warn("layersMutex is held while a listener was called."); |
| 237 | } |
| 238 | if (!Thread.holdsLock(layersChangingMutex)) { |
| 239 | Main.warn("layersChangingMutex is not held while a listener was called."); |
238 | 240 | } |
239 | 241 | } |
240 | 242 | |
241 | 243 | /** |
242 | | * A list of all layers currently loaded. Locked by {@link #layerLock}. |
| 244 | * A list of all layers currently loaded. Locked by {@link #layersMutex}. |
243 | 245 | */ |
244 | 246 | private final transient List<Layer> layers = new ArrayList<>(); |
245 | 247 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
247 | 249 | * This lock manages concurrent access to {@link #layers}, |
248 | 250 | * {@link #editLayer} and {@link #activeLayer}. |
249 | 251 | * <p> |
250 | | * The read lock is always held while those fields are read or while layer change listeners are fired. |
| 252 | * This mutex should never be held when calling "out" (to other classes, listeners, ...) |
| 253 | */ |
| 254 | private final transient Object layersMutex = new Object(); |
| 255 | |
| 256 | /** |
| 257 | * This is a mutex that only locks all methods that change the layers. It is locked in a way that there may be no more changes to layers in other threads while the listeners of the current change are called. Reads are possible in all threads during this phase. This is necessary because many listeners use {@link GuiHelper#runInEDTAndWait(Runnable)} |
251 | 258 | */ |
252 | | private final ReentrantReadWriteLock layerLock = new ReentrantReadWriteLock(); |
| 259 | private final transient Object layersChangingMutex = new Object(); |
253 | 260 | |
254 | 261 | /** |
255 | 262 | * The play head marker: there is only one of these so it isn't in any specific layer |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
257 | 264 | public transient PlayHeadMarker playHeadMarker = null; |
258 | 265 | |
259 | 266 | /** |
260 | | * The layer from the layers list that is currently active. Locked by {@link #layerLock}. |
| 267 | * The layer from the layers list that is currently active. Locked by {@link #layersMutex}. |
261 | 268 | */ |
262 | 269 | private transient Layer activeLayer; |
263 | 270 | |
264 | 271 | /** |
265 | | * The edit layer is the current active data layer. Locked by {@link #layerLock}. |
| 272 | * The edit layer is the current active data layer. Locked by {@link #layersMutex}. |
266 | 273 | */ |
267 | 274 | private transient OsmDataLayer editLayer; |
268 | 275 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
375 | 382 | * @param layer the GPX layer |
376 | 383 | */ |
377 | 384 | protected void addGpxLayer(GpxLayer layer) { |
378 | | layerLock.writeLock().lock(); |
379 | | try { |
| 385 | synchronized (layersMutex) { |
380 | 386 | if (layers.isEmpty()) { |
381 | 387 | layers.add(layer); |
382 | 388 | return; |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
392 | 398 | } |
393 | 399 | } |
394 | 400 | layers.add(0, layer); |
395 | | } finally { |
396 | | layerLock.writeLock().unlock(); |
397 | 401 | } |
398 | 402 | } |
399 | 403 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
404 | 408 | */ |
405 | 409 | public void addLayer(Layer layer) { |
406 | 410 | boolean isOsmDataLayer = layer instanceof OsmDataLayer; |
407 | | layerLock.writeLock().lock(); |
408 | | layerLock.readLock().lock(); |
409 | 411 | EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class); |
410 | | Layer oldActiveLayer = activeLayer; |
411 | | OsmDataLayer oldEditLayer = editLayer; |
412 | | try { |
413 | | try { |
| 412 | synchronized (layersChangingMutex) { |
| 413 | Layer oldActiveLayer; |
| 414 | OsmDataLayer oldEditLayer; |
| 415 | synchronized (layersMutex) { |
| 416 | oldActiveLayer = activeLayer; |
| 417 | oldEditLayer = editLayer; |
414 | 418 | if (layer instanceof MarkerLayer && playHeadMarker == null) { |
415 | 419 | playHeadMarker = PlayHeadMarker.create(); |
416 | 420 | } |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
435 | 439 | // autoselect the new layer |
436 | 440 | listenersToFire.addAll(setActiveLayer(layer, true)); |
437 | 441 | } |
438 | | } finally { |
439 | | layerLock.writeLock().unlock(); |
440 | 442 | } |
441 | 443 | |
442 | 444 | fireLayerAdded(layer); |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
447 | 449 | layer.addPropertyChangeListener(this); |
448 | 450 | Main.addProjectionChangeListener(layer); |
449 | 451 | AudioPlayer.reset(); |
450 | | } finally { |
451 | | layerLock.readLock().unlock(); |
452 | 452 | } |
453 | 453 | if (!listenersToFire.isEmpty()) { |
454 | 454 | repaint(); |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
457 | 457 | |
458 | 458 | @Override |
459 | 459 | protected DataSet getCurrentDataSet() { |
460 | | layerLock.readLock().lock(); |
461 | | try { |
| 460 | synchronized (layersMutex) { |
462 | 461 | if (editLayer != null) |
463 | 462 | return editLayer.data; |
464 | 463 | else |
465 | 464 | return null; |
466 | | } finally { |
467 | | layerLock.readLock().unlock(); |
468 | 465 | } |
469 | 466 | } |
470 | 467 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
474 | 471 | * @return true if the active data layer (edit layer) is drawable, false otherwise |
475 | 472 | */ |
476 | 473 | public boolean isActiveLayerDrawable() { |
477 | | layerLock.readLock().lock(); |
478 | | try { |
| 474 | synchronized (layersMutex) { |
479 | 475 | return editLayer != null; |
480 | | } finally { |
481 | | layerLock.readLock().unlock(); |
482 | 476 | } |
483 | 477 | } |
484 | 478 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
488 | 482 | * @return true if the active data layer (edit layer) is visible, false otherwise |
489 | 483 | */ |
490 | 484 | public boolean isActiveLayerVisible() { |
491 | | layerLock.readLock().lock(); |
492 | | try { |
| 485 | synchronized (layersMutex) { |
493 | 486 | return isActiveLayerDrawable() && editLayer.isVisible(); |
494 | | } finally { |
495 | | layerLock.readLock().unlock(); |
496 | 487 | } |
497 | 488 | } |
498 | 489 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
529 | 520 | * @param layer The layer to remove |
530 | 521 | */ |
531 | 522 | public void removeLayer(Layer layer) { |
532 | | layerLock.writeLock().lock(); |
533 | | layerLock.readLock().lock(); |
534 | | |
535 | 523 | EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class); |
536 | | Layer oldActiveLayer = activeLayer; |
537 | | OsmDataLayer oldEditLayer = editLayer; |
538 | | try { |
539 | | try { |
| 524 | |
| 525 | synchronized (layersChangingMutex) { |
| 526 | Layer oldActiveLayer; |
| 527 | OsmDataLayer oldEditLayer; |
| 528 | synchronized (layersMutex) { |
| 529 | oldActiveLayer = activeLayer; |
| 530 | oldEditLayer = editLayer; |
540 | 531 | List<Layer> layersList = new ArrayList<>(layers); |
541 | 532 | |
542 | 533 | if (!layersList.remove(layer)) |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
555 | 546 | layers.remove(layer); |
556 | 547 | Main.removeProjectionChangeListener(layer); |
557 | 548 | |
558 | | } finally { |
559 | | layerLock.writeLock().unlock(); |
560 | 549 | } |
561 | 550 | onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire); |
562 | 551 | fireLayerRemoved(layer); |
563 | 552 | layer.removePropertyChangeListener(this); |
564 | 553 | layer.destroy(); |
565 | 554 | AudioPlayer.reset(); |
566 | | } finally { |
567 | | layerLock.readLock().unlock(); |
568 | 555 | } |
569 | 556 | repaint(); |
570 | 557 | } |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
600 | 587 | * @param pos The new position of the layer |
601 | 588 | */ |
602 | 589 | public void moveLayer(Layer layer, int pos) { |
603 | | layerLock.writeLock().lock(); |
604 | | layerLock.readLock().lock(); |
605 | | EnumSet<LayerListenerType> listenersToFire; |
606 | | Layer oldActiveLayer = activeLayer; |
607 | | OsmDataLayer oldEditLayer = editLayer; |
608 | | try { |
609 | | try { |
| 590 | synchronized (layersChangingMutex) { |
| 591 | EnumSet<LayerListenerType> listenersToFire; |
| 592 | Layer oldActiveLayer; |
| 593 | OsmDataLayer oldEditLayer; |
| 594 | synchronized (layersMutex) { |
| 595 | oldActiveLayer = activeLayer; |
| 596 | oldEditLayer = editLayer; |
610 | 597 | int curLayerPos = layers.indexOf(layer); |
611 | 598 | if (curLayerPos == -1) |
612 | 599 | throw new IllegalArgumentException(tr("Layer not in list.")); |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
619 | 606 | layers.add(pos, layer); |
620 | 607 | } |
621 | 608 | listenersToFire = setEditLayer(layers); |
622 | | } finally { |
623 | | layerLock.writeLock().unlock(); |
624 | 609 | } |
625 | 610 | onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire); |
626 | 611 | AudioPlayer.reset(); |
627 | | } finally { |
628 | | layerLock.readLock().unlock(); |
629 | 612 | } |
630 | 613 | repaint(); |
631 | 614 | } |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
638 | 621 | */ |
639 | 622 | public int getLayerPos(Layer layer) { |
640 | 623 | int curLayerPos; |
641 | | layerLock.readLock().lock(); |
642 | | try { |
| 624 | synchronized (layersMutex) { |
643 | 625 | curLayerPos = layers.indexOf(layer); |
644 | | } finally { |
645 | | layerLock.readLock().unlock(); |
646 | 626 | } |
647 | 627 | if (curLayerPos == -1) |
648 | 628 | throw new IllegalArgumentException(tr("Layer not in list.")); |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
659 | 639 | * first, layer with the highest Z-Order last. |
660 | 640 | */ |
661 | 641 | public List<Layer> getVisibleLayersInZOrder() { |
662 | | layerLock.readLock().lock(); |
663 | | try { |
664 | | List<Layer> ret = new ArrayList<>(); |
| 642 | List<Layer> ret = new ArrayList<>(); |
| 643 | synchronized (layersMutex) { |
665 | 644 | // This is set while we delay the addition of the active layer. |
666 | 645 | boolean activeLayerDelayed = false; |
667 | 646 | for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) { |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
685 | 664 | ret.add(activeLayer); |
686 | 665 | } |
687 | 666 | return ret; |
688 | | } finally { |
689 | | layerLock.readLock().unlock(); |
690 | 667 | } |
691 | 668 | } |
692 | 669 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
875 | 852 | * @return An unmodifiable collection of all layers |
876 | 853 | */ |
877 | 854 | public Collection<Layer> getAllLayers() { |
878 | | layerLock.readLock().lock(); |
879 | | try { |
| 855 | synchronized (layersMutex) { |
880 | 856 | return Collections.unmodifiableCollection(new ArrayList<>(layers)); |
881 | | } finally { |
882 | | layerLock.readLock().unlock(); |
883 | 857 | } |
884 | 858 | } |
885 | 859 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
887 | 861 | * @return An unmodifiable ordered list of all layers |
888 | 862 | */ |
889 | 863 | public List<Layer> getAllLayersAsList() { |
890 | | layerLock.readLock().lock(); |
891 | | try { |
| 864 | synchronized (layersMutex) { |
892 | 865 | return Collections.unmodifiableList(new ArrayList<>(layers)); |
893 | | } finally { |
894 | | layerLock.readLock().unlock(); |
895 | 866 | } |
896 | 867 | } |
897 | 868 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
903 | 874 | * List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class); |
904 | 875 | * </pre> |
905 | 876 | * |
906 | | * @param ofType The layer type. |
907 | 877 | * @return an unmodifiable list of layers of a certain type. |
908 | 878 | */ |
909 | 879 | public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) { |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
916 | 886 | * @return the number of layers managed by this map view |
917 | 887 | */ |
918 | 888 | public int getNumLayers() { |
919 | | layerLock.readLock().lock(); |
920 | | try { |
| 889 | synchronized (layersMutex) { |
921 | 890 | return layers.size(); |
922 | | } finally { |
923 | | layerLock.readLock().unlock(); |
924 | 891 | } |
925 | 892 | } |
926 | 893 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
936 | 903 | /** |
937 | 904 | * Sets the active edit layer. |
938 | 905 | * <p> |
939 | | * You must own a write {@link #layerLock} when calling this method. |
| 906 | * You must own {@link #layersMutex} when calling this method. |
940 | 907 | * @param layersList A list to select that layer from. |
941 | 908 | * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)} |
942 | 909 | */ |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
946 | 913 | // Set new edit layer |
947 | 914 | if (newEditLayer != editLayer) { |
948 | 915 | if (newEditLayer == null) { |
949 | | // Note: Unsafe to call while layer write lock is held. |
| 916 | // Note: Unsafe to call while layersMutex is held. |
950 | 917 | getCurrentDataSet().setSelected(); |
951 | 918 | } |
952 | 919 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
984 | 951 | * @throws IllegalArgumentException if layer is not in the lis of layers |
985 | 952 | */ |
986 | 953 | public void setActiveLayer(Layer layer) { |
987 | | layerLock.writeLock().lock(); |
988 | | layerLock.readLock().lock(); |
989 | | EnumSet<LayerListenerType> listenersToFire; |
990 | | Layer oldActiveLayer = activeLayer; |
991 | | OsmDataLayer oldEditLayer = editLayer; |
992 | | try { |
993 | | try { |
| 954 | synchronized (layersChangingMutex) { |
| 955 | EnumSet<LayerListenerType> listenersToFire; |
| 956 | Layer oldActiveLayer; |
| 957 | OsmDataLayer oldEditLayer; |
| 958 | synchronized (layersMutex) { |
| 959 | oldActiveLayer = activeLayer; |
| 960 | oldEditLayer = editLayer; |
994 | 961 | listenersToFire = setActiveLayer(layer, true); |
995 | | } finally { |
996 | | layerLock.writeLock().unlock(); |
997 | 962 | } |
998 | 963 | onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire); |
999 | | } finally { |
1000 | | layerLock.readLock().unlock(); |
1001 | 964 | } |
1002 | 965 | repaint(); |
1003 | 966 | } |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
1030 | 993 | * @return the currently active layer (may be null) |
1031 | 994 | */ |
1032 | 995 | public Layer getActiveLayer() { |
1033 | | layerLock.readLock().lock(); |
1034 | | try { |
| 996 | synchronized (layersMutex) { |
1035 | 997 | return activeLayer; |
1036 | | } finally { |
1037 | | layerLock.readLock().unlock(); |
1038 | 998 | } |
1039 | 999 | } |
1040 | 1000 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
1089 | 1049 | * @return the current edit layer. May be null. |
1090 | 1050 | */ |
1091 | 1051 | public OsmDataLayer getEditLayer() { |
1092 | | layerLock.readLock().lock(); |
1093 | | try { |
| 1052 | synchronized (layersMutex) { |
1094 | 1053 | return editLayer; |
1095 | | } finally { |
1096 | | layerLock.readLock().unlock(); |
1097 | 1054 | } |
1098 | 1055 | } |
1099 | 1056 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
1104 | 1061 | * @return true if the list of layers managed by this map view contain layer |
1105 | 1062 | */ |
1106 | 1063 | public boolean hasLayer(Layer layer) { |
1107 | | layerLock.readLock().lock(); |
1108 | | try { |
| 1064 | synchronized (layersMutex) { |
1109 | 1065 | return layers.contains(layer); |
1110 | | } finally { |
1111 | | layerLock.readLock().unlock(); |
1112 | 1066 | } |
1113 | 1067 | } |
1114 | 1068 | |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
1173 | 1127 | */ |
1174 | 1128 | protected void refreshTitle() { |
1175 | 1129 | if (Main.parent != null) { |
1176 | | layerLock.readLock().lock(); |
1177 | | try { |
| 1130 | synchronized (layersMutex) { |
1178 | 1131 | boolean dirty = editLayer != null && |
1179 | 1132 | (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); |
1180 | 1133 | ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); |
1181 | 1134 | ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty); |
1182 | | } finally { |
1183 | | layerLock.readLock().unlock(); |
1184 | 1135 | } |
1185 | 1136 | } |
1186 | 1137 | } |
… |
… |
implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
|
1206 | 1157 | if (mapMover != null) { |
1207 | 1158 | mapMover.destroy(); |
1208 | 1159 | } |
1209 | | layerLock.writeLock().lock(); |
1210 | | try { |
1211 | | activeLayer = null; |
1212 | | changedLayer = null; |
1213 | | editLayer = null; |
1214 | | layers.clear(); |
1215 | | } finally { |
1216 | | layerLock.writeLock().unlock(); |
| 1160 | synchronized (layersChangingMutex) { |
| 1161 | synchronized (layersMutex) { |
| 1162 | activeLayer = null; |
| 1163 | changedLayer = null; |
| 1164 | editLayer = null; |
| 1165 | layers.clear(); |
| 1166 | } |
1217 | 1167 | } |
1218 | 1168 | nonChangedLayers.clear(); |
1219 | 1169 | synchronized (temporaryLayers) { |