From 3d6ca162672f576b60d079c2b0f6dbf545d0c756 Mon Sep 17 00:00:00 2001
From: Michael Zangl <michael.zangl@student.kit.edu>
Date: Sat, 2 Jan 2016 14:14:56 +0100
Subject: [PATCH 1/2] Fixed keys array access on some places
---
.../josm/data/osm/AbstractPrimitive.java | 69 +++++++++++++++-------
.../openstreetmap/josm/data/osm/OsmPrimitive.java | 36 +++++------
2 files changed, 65 insertions(+), 40 deletions(-)
diff --git a/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java b/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
index bb7127c..c716f23 100644
|
a
|
b
|
public abstract class AbstractPrimitive implements IPrimitive {
|
| 481 | 481 | * Keys handling |
| 482 | 482 | ------------*/ |
| 483 | 483 | |
| 484 | | // Note that all methods that read keys first make local copy of keys array reference. This is to ensure thread safety - reading |
| 485 | | // doesn't have to be locked so it's possible that keys array will be modified. But all write methods make copy of keys array so |
| 486 | | // the array itself will be never modified - only reference will be changed |
| 487 | | |
| 488 | 484 | /** |
| 489 | 485 | * The key/value list for this primitive. |
| | 486 | * <p> |
| | 487 | * Note that the keys field is synchronized using RCU. |
| | 488 | * Writes to it are not synchronized by this object, the writers have to synchronize writes themselves. |
| | 489 | * <p> |
| | 490 | * In short this means that you should not rely on this variable being the same value when read again and your should always copy it on writes. |
| 490 | 491 | */ |
| 491 | | protected String[] keys; |
| | 492 | protected volatile String[] keys; |
| 492 | 493 | |
| 493 | 494 | /** |
| 494 | 495 | * Replies the map of key/value pairs. Never replies null. The map can be empty, though. |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 530 | 531 | * Sets the keys of this primitives to the key/value pairs in <code>keys</code>. |
| 531 | 532 | * Old key/value pairs are removed. |
| 532 | 533 | * If <code>keys</code> is null, clears existing key/value pairs. |
| | 534 | * <p> |
| | 535 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| 533 | 536 | * |
| 534 | 537 | * @param keys the key/value pairs to set. If null, removes all existing key/value pairs. |
| 535 | 538 | */ |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 554 | 557 | /** |
| 555 | 558 | * Set the given value to the given key. If key is null, does nothing. If value is null, |
| 556 | 559 | * removes the key and behaves like {@link #remove(String)}. |
| | 560 | * <p> |
| | 561 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| 557 | 562 | * |
| 558 | 563 | * @param key The key, for which the value is to be set. Can be null or empty, does nothing in this case. |
| 559 | 564 | * @param value The value for the key. If null, removes the respective key/value pair. |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 571 | 576 | keys = new String[] {key, value}; |
| 572 | 577 | keysChangedImpl(originalKeys); |
| 573 | 578 | } else { |
| 574 | | for (int i = 0; i < keys.length; i += 2) { |
| 575 | | if (keys[i].equals(key)) { |
| 576 | | // This modifies the keys array but it doesn't make it invalidate for any time so its ok (see note no top) |
| 577 | | keys[i+1] = value; |
| 578 | | keysChangedImpl(originalKeys); |
| 579 | | return; |
| 580 | | } |
| | 579 | int keyIndex = indexOfKey(keys, key); |
| | 580 | int tagArrayLength = keys.length; |
| | 581 | if (keyIndex < 0) { |
| | 582 | keyIndex = tagArrayLength; |
| | 583 | tagArrayLength += 2; |
| 581 | 584 | } |
| 582 | | String[] newKeys = Arrays.copyOf(keys, keys.length + 2); |
| 583 | | newKeys[keys.length] = key; |
| 584 | | newKeys[keys.length + 1] = value; |
| | 585 | |
| | 586 | // Do not try to optimize this array creation if the key already exists. |
| | 587 | // We would need to convert the keys array to be an AtomicReferenceArray |
| | 588 | // Or we would at least need a volatile write after the array was modified to |
| | 589 | // ensure that changes are visible by other threads. |
| | 590 | String[] newKeys = Arrays.copyOf(keys, tagArrayLength); |
| | 591 | newKeys[keyIndex] = key; |
| | 592 | newKeys[keyIndex + 1] = value; |
| 585 | 593 | keys = newKeys; |
| 586 | 594 | keysChangedImpl(originalKeys); |
| 587 | 595 | } |
| 588 | 596 | } |
| 589 | 597 | |
| 590 | 598 | /** |
| | 599 | * Scans a key/value array for a given key. |
| | 600 | * @param keys The key array. It is not modified. It may be null to indicate an emtpy array. |
| | 601 | * @param key The key to search for. |
| | 602 | * @return The position of that key in the keys array - which is always a multiple of 2 - or -1 if it was not found. |
| | 603 | */ |
| | 604 | private static int indexOfKey(String[] keys, String key) { |
| | 605 | if (keys == null) { |
| | 606 | return -1; |
| | 607 | } |
| | 608 | for (int i = 0; i < keys.length; i += 2) { |
| | 609 | if (keys[i].equals(key)) { |
| | 610 | return i; |
| | 611 | } |
| | 612 | } |
| | 613 | return -1; |
| | 614 | } |
| | 615 | |
| | 616 | /** |
| 591 | 617 | * Remove the given key from the list |
| | 618 | * <p> |
| | 619 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| 592 | 620 | * |
| 593 | 621 | * @param key the key to be removed. Ignored, if key is null. |
| 594 | 622 | */ |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 617 | 645 | |
| 618 | 646 | /** |
| 619 | 647 | * Removes all keys from this primitive. |
| | 648 | * <p> |
| | 649 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| 620 | 650 | */ |
| 621 | 651 | @Override |
| 622 | 652 | public void removeAll() { |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 680 | 710 | } |
| 681 | 711 | |
| 682 | 712 | public final int getNumKeys() { |
| | 713 | String[] keys = this.keys; |
| 683 | 714 | return keys == null ? 0 : keys.length / 2; |
| 684 | 715 | } |
| 685 | 716 | |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 718 | 749 | * @return true, if his primitive has a tag with key <code>key</code> |
| 719 | 750 | */ |
| 720 | 751 | public boolean hasKey(String key) { |
| 721 | | String[] keys = this.keys; |
| 722 | | if (key == null) return false; |
| 723 | | if (keys == null) return false; |
| 724 | | for (int i = 0; i < keys.length; i += 2) { |
| 725 | | if (keys[i].equals(key)) return true; |
| 726 | | } |
| 727 | | return false; |
| | 752 | return key != null && indexOfKey(keys, key) >= 0; |
| 728 | 753 | } |
| 729 | 754 | |
| 730 | 755 | /** |
diff --git a/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java b/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
index 9d5cb5d..b90e896 100644
|
a
|
b
|
public abstract class OsmPrimitive extends AbstractPrimitive implements Comparab
|
| 775 | 775 | |
| 776 | 776 | /** |
| 777 | 777 | * Returns {@link #getKeys()} for which {@code key} does not fulfill {@link #isUninterestingKey}. |
| 778 | | * @return list of interesting tags |
| | 778 | * @return A map of interesting tags |
| 779 | 779 | */ |
| 780 | 780 | public Map<String, String> getInterestingTags() { |
| 781 | 781 | Map<String, String> result = new HashMap<>(); |
| … |
… |
public abstract class OsmPrimitive extends AbstractPrimitive implements Comparab
|
| 832 | 832 | } |
| 833 | 833 | |
| 834 | 834 | private void updateTagged() { |
| 835 | | if (keys != null) { |
| 836 | | for (String key: keySet()) { |
| 837 | | // 'area' is not really uninteresting (putting it in that list may have unpredictable side effects) |
| 838 | | // but it's clearly not enough to consider an object as tagged (see #9261) |
| 839 | | if (!isUninterestingKey(key) && !"area".equals(key)) { |
| 840 | | updateFlagsNoLock(FLAG_TAGGED, true); |
| 841 | | return; |
| 842 | | } |
| | 835 | for (String key: keySet()) { |
| | 836 | // 'area' is not really uninteresting (putting it in that list may have unpredictable side effects) |
| | 837 | // but it's clearly not enough to consider an object as tagged (see #9261) |
| | 838 | if (!isUninterestingKey(key) && !"area".equals(key)) { |
| | 839 | updateFlagsNoLock(FLAG_TAGGED, true); |
| | 840 | return; |
| 843 | 841 | } |
| 844 | 842 | } |
| 845 | 843 | updateFlagsNoLock(FLAG_TAGGED, false); |
| 846 | 844 | } |
| 847 | 845 | |
| 848 | 846 | private void updateAnnotated() { |
| 849 | | if (keys != null) { |
| 850 | | for (String key: keySet()) { |
| 851 | | if (getWorkInProgressKeys().contains(key)) { |
| 852 | | updateFlagsNoLock(FLAG_ANNOTATED, true); |
| 853 | | return; |
| 854 | | } |
| | 847 | for (String key: keySet()) { |
| | 848 | if (getWorkInProgressKeys().contains(key)) { |
| | 849 | updateFlagsNoLock(FLAG_ANNOTATED, true); |
| | 850 | return; |
| 855 | 851 | } |
| 856 | 852 | } |
| 857 | 853 | updateFlagsNoLock(FLAG_ANNOTATED, false); |
| … |
… |
public abstract class OsmPrimitive extends AbstractPrimitive implements Comparab
|
| 1190 | 1186 | // We cannot directly use Arrays.equals(keys, other.keys) as keys is not ordered by key |
| 1191 | 1187 | // but we can at least check if both arrays are null or of the same size before creating |
| 1192 | 1188 | // and comparing the key maps (costly operation, see #7159) |
| 1193 | | return (keys == null && other.keys == null) |
| 1194 | | || (keys != null && other.keys != null && keys.length == other.keys.length |
| 1195 | | && (keys.length == 0 || getInterestingTags().equals(other.getInterestingTags()))); |
| | 1189 | // Note: This implementation is just broken. myKeys.length == 0 should not happen and |
| | 1190 | // myKeys.length == otherKeys.length is not required for the same interesting tags. |
| | 1191 | String[] myKeys = keys; |
| | 1192 | String[] otherKeys = other.keys; |
| | 1193 | return (myKeys == null && otherKeys == null) |
| | 1194 | || (myKeys != null && otherKeys != null && myKeys.length == otherKeys.length |
| | 1195 | && (myKeys.length == 0 || getInterestingTags().equals(other.getInterestingTags()))); |
| 1196 | 1196 | } |
| 1197 | 1197 | |
| 1198 | 1198 | /** |
--
1.9.1
From 23bb9c0e031d5407de2562f56eb7b339a4363c11 Mon Sep 17 00:00:00 2001
From: Michael Zangl <michael.zangl@student.kit.edu>
Date: Sat, 2 Jan 2016 17:31:27 +0100
Subject: [PATCH 2/2] Fixed checkstyle. added links for RCU
---
.../josm/data/osm/AbstractPrimitive.java | 24 +++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java b/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
index c716f23..90ba106 100644
|
a
|
b
|
import java.util.Map;
|
| 14 | 14 | import java.util.Map.Entry; |
| 15 | 15 | import java.util.Objects; |
| 16 | 16 | import java.util.Set; |
| | 17 | import java.util.concurrent.CopyOnWriteArrayList; |
| 17 | 18 | import java.util.concurrent.atomic.AtomicLong; |
| 18 | 19 | |
| 19 | 20 | import org.openstreetmap.josm.tools.LanguageInfo; |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 487 | 488 | * Note that the keys field is synchronized using RCU. |
| 488 | 489 | * Writes to it are not synchronized by this object, the writers have to synchronize writes themselves. |
| 489 | 490 | * <p> |
| 490 | | * In short this means that you should not rely on this variable being the same value when read again and your should always copy it on writes. |
| | 491 | * In short this means that you should not rely on this variable being the same value when read again and your should always |
| | 492 | * copy it on writes. |
| | 493 | * <p> |
| | 494 | * Further reading: |
| | 495 | * <ul> |
| | 496 | * <li> The {@link CopyOnWriteArrayList} class |
| | 497 | * <li> http://stackoverflow.com/questions/2950871/how-can-copyonwritearraylist-be-thread-safe |
| | 498 | * <li> https://en.wikipedia.org/wiki/Read-copy-update |
| | 499 | * (mind that we have a Garbage collector, rcu_assign_pointer and rcu_dereference are ensured by the volatile keyword) |
| | 500 | * </ul> |
| 491 | 501 | */ |
| 492 | 502 | protected volatile String[] keys; |
| 493 | 503 | |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 532 | 542 | * Old key/value pairs are removed. |
| 533 | 543 | * If <code>keys</code> is null, clears existing key/value pairs. |
| 534 | 544 | * <p> |
| 535 | | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| | 545 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used |
| | 546 | * from multiple threads. |
| 536 | 547 | * |
| 537 | 548 | * @param keys the key/value pairs to set. If null, removes all existing key/value pairs. |
| 538 | 549 | */ |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 558 | 569 | * Set the given value to the given key. If key is null, does nothing. If value is null, |
| 559 | 570 | * removes the key and behaves like {@link #remove(String)}. |
| 560 | 571 | * <p> |
| 561 | | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| | 572 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used |
| | 573 | * from multiple threads. |
| 562 | 574 | * |
| 563 | 575 | * @param key The key, for which the value is to be set. Can be null or empty, does nothing in this case. |
| 564 | 576 | * @param value The value for the key. If null, removes the respective key/value pair. |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 616 | 628 | /** |
| 617 | 629 | * Remove the given key from the list |
| 618 | 630 | * <p> |
| 619 | | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| | 631 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used |
| | 632 | * from multiple threads. |
| 620 | 633 | * |
| 621 | 634 | * @param key the key to be removed. Ignored, if key is null. |
| 622 | 635 | */ |
| … |
… |
public abstract class AbstractPrimitive implements IPrimitive {
|
| 646 | 659 | /** |
| 647 | 660 | * Removes all keys from this primitive. |
| 648 | 661 | * <p> |
| 649 | | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used from multiple threads. |
| | 662 | * Note that this method, like all methods that modify keys, is not synchronized and may lead to data corruption when being used |
| | 663 | * from multiple threads. |
| 650 | 664 | */ |
| 651 | 665 | @Override |
| 652 | 666 | public void removeAll() { |