Ignore:
Timestamp:
2010-06-27T17:07:49+02:00 (14 years ago)
Author:
jttt
Message:

Thread safe Dataset and OsmPrimitive, read/write lock for dataset

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r3342 r3348  
    345345    }
    346346
     347    protected boolean writeLock() {
     348        if (dataSet != null) {
     349            dataSet.beginUpdate();
     350            return true;
     351        } else
     352            return false;
     353    }
     354
     355    protected void writeUnlock(boolean locked) {
     356        if (locked) {
     357            // It shouldn't be possible for dataset to become null because method calling setDataset would need write lock which is owned by this thread
     358            dataSet.endUpdate();
     359        }
     360    }
     361
     362
    347363    /*-------------------
    348364     * OTHER PROPERTIES
    349      *-------------------/
     365     *-------------------*/
    350366
    351367    /**
     
    392408     */
    393409    public long getId() {
     410        long id = this.id;
    394411        return id >= 0?id:0;
    395412    }
     
    434451     */
    435452    public void setOsmId(long id, int version) {
    436         if (id <= 0)
    437             throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
    438         if (version <= 0)
    439             throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
    440         if (dataSet != null && id != this.id) {
    441             DataSet datasetCopy = dataSet;
    442             // Reindex primitive
    443             datasetCopy.removePrimitive(this);
     453        boolean locked = writeLock();
     454        try {
     455            if (id <= 0)
     456                throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
     457            if (version <= 0)
     458                throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
     459            if (dataSet != null && id != this.id) {
     460                DataSet datasetCopy = dataSet;
     461                // Reindex primitive
     462                datasetCopy.removePrimitive(this);
     463                this.id = id;
     464                datasetCopy.addPrimitive(this);
     465            }
    444466            this.id = id;
    445             datasetCopy.addPrimitive(this);
    446         }
    447         this.id = id;
    448         this.version = version;
    449         this.setIncomplete(false);
     467            this.version = version;
     468            this.setIncomplete(false);
     469        } finally {
     470            writeUnlock(locked);
     471        }
    450472    }
    451473
     
    462484        if (dataSet != null)
    463485            throw new DataIntegrityProblemException("Method cannot be called after primitive was added to the dataset");
     486
     487        // Not part of dataset - no lock necessary
    464488        this.id = generateUniqueId();
    465489        this.version = 0;
     
    483507     */
    484508    public void setUser(User user) {
    485         this.user = user;
     509        boolean locked = writeLock();
     510        try {
     511            this.user = user;
     512        } finally {
     513            writeUnlock(locked);
     514        }
    486515    }
    487516
     
    506535     */
    507536    public void setChangesetId(int changesetId) throws IllegalStateException, IllegalArgumentException {
    508         if (this.changesetId == changesetId)
    509             return;
    510         if (changesetId < 0)
    511             throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' >= 0 expected, got {1}", "changesetId", changesetId));
    512         if (isNew() && changesetId > 0)
    513             throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
    514         int old = this.changesetId;
    515         this.changesetId = changesetId;
    516         if (dataSet != null) {
    517             dataSet.fireChangesetIdChanged(this, old, changesetId);
     537        boolean locked = writeLock();
     538        try {
     539            if (this.changesetId == changesetId)
     540                return;
     541            if (changesetId < 0)
     542                throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' >= 0 expected, got {1}", "changesetId", changesetId));
     543            if (isNew() && changesetId > 0)
     544                throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
     545
     546            int old = this.changesetId;
     547            this.changesetId = changesetId;
     548            if (dataSet != null) {
     549                dataSet.fireChangesetIdChanged(this, old, changesetId);
     550            }
     551        } finally {
     552            writeUnlock(locked);
    518553        }
    519554    }
     
    529564
    530565    public void setTimestamp(Date timestamp) {
    531         this.timestamp = (int)(timestamp.getTime() / 1000);
     566        boolean locked = writeLock();
     567        try {
     568            this.timestamp = (int)(timestamp.getTime() / 1000);
     569        } finally {
     570            writeUnlock(locked);
     571        }
    532572    }
    533573
     
    554594    private volatile short flags = FLAG_VISIBLE;   // visible per default
    555595
    556     private void updateFlags(int flag, boolean value) {
     596    private void updateFlagsNoLock(int flag, boolean value) {
    557597        if (value) {
    558598            flags |= flag;
     
    562602    }
    563603
     604    private void updateFlags(int flag, boolean value) {
     605        boolean locked = writeLock();
     606        try {
     607            updateFlagsNoLock(flag, value);
     608        } finally {
     609            writeUnlock(locked);
     610        }
     611    }
     612
    564613    /**
    565614     * Make the primitive disabled (e.g. if a filter applies).
     
    569618     */
    570619    public void setDisabledState(boolean hide) {
    571         flags |= FLAG_DISABLED;
    572         if (hide) {
    573             flags |= FLAG_HIDE_IF_DISABLED;
    574         } else {
    575             flags &= ~FLAG_HIDE_IF_DISABLED;
     620        boolean locked = writeLock();
     621        try {
     622            updateFlagsNoLock(FLAG_DISABLED, true);
     623            updateFlagsNoLock(FLAG_HIDE_IF_DISABLED, hide);
     624        } finally {
     625            writeUnlock(locked);
    576626        }
    577627    }
     
    688738     */
    689739    public void setVisible(boolean visible) throws IllegalStateException{
    690         if (isNew() && visible == false)
    691             throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
    692         updateFlags(FLAG_VISIBLE, visible);
     740        boolean locked = writeLock();
     741        try {
     742            if (isNew() && visible == false)
     743                throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
     744            updateFlagsNoLock(FLAG_VISIBLE, visible);
     745        } finally {
     746            writeUnlock(locked);
     747        }
    693748    }
    694749
     
    701756     */
    702757    public void setDeleted(boolean deleted) {
    703         updateFlags(FLAG_DELETED, deleted);
    704         setModified(deleted ^ !isVisible());
    705         if (dataSet != null) {
    706             if (deleted) {
    707                 dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
    708             } else {
    709                 dataSet.firePrimitivesAdded(Collections.singleton(this), false);
    710             }
     758        boolean locked = writeLock();
     759        try {
     760            updateFlagsNoLock(FLAG_DELETED, deleted);
     761            setModified(deleted ^ !isVisible());
     762            if (dataSet != null) {
     763                if (deleted) {
     764                    dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
     765                } else {
     766                    dataSet.firePrimitivesAdded(Collections.singleton(this), false);
     767                }
     768            }
     769        } finally {
     770            writeUnlock(locked);
    711771        }
    712772    }
     
    718778     */
    719779    private void setIncomplete(boolean incomplete) {
    720         if (dataSet != null && incomplete != this.isIncomplete()) {
    721             if (incomplete) {
    722                 dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
    723             } else {
    724                 dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
    725             }
    726         }
    727         updateFlags(FLAG_INCOMPLETE, incomplete);
     780        boolean locked = writeLock();
     781        try {
     782            if (dataSet != null && incomplete != this.isIncomplete()) {
     783                if (incomplete) {
     784                    dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
     785                } else {
     786                    dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
     787                }
     788            }
     789            updateFlagsNoLock(FLAG_INCOMPLETE, incomplete);
     790        }  finally {
     791            writeUnlock(locked);
     792        }
    728793    }
    729794
     
    842907            for (String key: keySet()) {
    843908                if (!isUninterestingKey(key)) {
    844                     updateFlags(FLAG_TAGGED, true);
     909                    updateFlagsNoLock(FLAG_TAGGED, true);
    845910                    return;
    846911                }
    847912            }
    848913        }
    849         updateFlags(FLAG_TAGGED, false);
     914        updateFlagsNoLock(FLAG_TAGGED, false);
    850915    }
    851916
     
    871936        }
    872937
    873         updateFlags(FLAG_DIRECTION_REVERSED, directionReversed);
    874         updateFlags(FLAG_HAS_DIRECTIONS, hasDirections);
     938        updateFlagsNoLock(FLAG_DIRECTION_REVERSED, directionReversed);
     939        updateFlagsNoLock(FLAG_HAS_DIRECTIONS, hasDirections);
    875940    }
    876941
     
    890955     ------------*/
    891956
     957    // Note that all methods that read keys first make local copy of keys array reference. This is to ensure thread safety - reading
     958    // 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
     959    // the array itself will be never modified - only reference will be changed
     960
    892961    /**
    893962     * The key/value list for this primitive.
     
    901970     * @return tags of this primitive. Changes made in returned map are not mapped
    902971     * back to the primitive, use setKeys() to modify the keys
    903      * @since 1924
    904972     */
    905973    public Map<String, String> getKeys() {
    906974        Map<String, String> result = new HashMap<String, String>();
     975        String[] keys = this.keys;
    907976        if (keys != null) {
    908977            for (int i=0; i<keys.length ; i+=2) {
     
    918987     *
    919988     * @param keys the key/value pairs to set. If null, removes all existing key/value pairs.
    920      * @since 1924
    921989     */
    922990    public void setKeys(Map<String, String> keys) {
    923         Map<String, String> originalKeys = getKeys();
    924         if (keys == null || keys.isEmpty()) {
    925             this.keys = null;
     991        boolean locked = writeLock();
     992        try {
     993            Map<String, String> originalKeys = getKeys();
     994            if (keys == null || keys.isEmpty()) {
     995                this.keys = null;
     996                keysChangedImpl(originalKeys);
     997                return;
     998            }
     999            String[] newKeys = new String[keys.size() * 2];
     1000            int index = 0;
     1001            for (Entry<String, String> entry:keys.entrySet()) {
     1002                newKeys[index++] = entry.getKey();
     1003                newKeys[index++] = entry.getValue();
     1004            }
     1005            this.keys = newKeys;
    9261006            keysChangedImpl(originalKeys);
    927             return;
    928         }
    929         String[] newKeys = new String[keys.size() * 2];
    930         int index = 0;
    931         for (Entry<String, String> entry:keys.entrySet()) {
    932             newKeys[index++] = entry.getKey();
    933             newKeys[index++] = entry.getValue();
    934         }
    935         this.keys = newKeys;
    936         keysChangedImpl(originalKeys);
     1007        } finally {
     1008            writeUnlock(locked);
     1009        }
    9371010    }
    9381011
     
    9471020     */
    9481021    public final void put(String key, String value) {
    949         Map<String, String> originalKeys = getKeys();
    950         if (key == null)
    951             return;
    952         else if (value == null) {
    953             remove(key);
    954         } else if (keys == null){
    955             keys = new String[] {key, value};
    956             keysChangedImpl(originalKeys);
    957         } else {
    958             for (int i=0; i<keys.length;i+=2) {
    959                 if (keys[i].equals(key)) {
    960                     keys[i+1] = value;
    961                     keysChangedImpl(originalKeys);
    962                     return;
     1022        boolean locked = writeLock();
     1023        try {
     1024            Map<String, String> originalKeys = getKeys();
     1025            if (key == null)
     1026                return;
     1027            else if (value == null) {
     1028                remove(key);
     1029            } else if (keys == null){
     1030                keys = new String[] {key, value};
     1031                keysChangedImpl(originalKeys);
     1032            } else {
     1033                for (int i=0; i<keys.length;i+=2) {
     1034                    if (keys[i].equals(key)) {
     1035                        keys[i+1] = value;  // This modifies the keys array but it doesn't make it invalidate for any time so its ok (see note no top)
     1036                        keysChangedImpl(originalKeys);
     1037                        return;
     1038                    }
    9631039                }
    964             }
    965             String[] newKeys = new String[keys.length + 2];
    966             for (int i=0; i< keys.length;i+=2) {
    967                 newKeys[i] = keys[i];
    968                 newKeys[i+1] = keys[i+1];
    969             }
    970             newKeys[keys.length] = key;
    971             newKeys[keys.length + 1] = value;
     1040                String[] newKeys = new String[keys.length + 2];
     1041                for (int i=0; i< keys.length;i+=2) {
     1042                    newKeys[i] = keys[i];
     1043                    newKeys[i+1] = keys[i+1];
     1044                }
     1045                newKeys[keys.length] = key;
     1046                newKeys[keys.length + 1] = value;
     1047                keys = newKeys;
     1048                keysChangedImpl(originalKeys);
     1049            }
     1050        } finally {
     1051            writeUnlock(locked);
     1052        }
     1053    }
     1054    /**
     1055     * Remove the given key from the list
     1056     *
     1057     * @param key  the key to be removed. Ignored, if key is null.
     1058     */
     1059    public final void remove(String key) {
     1060        boolean locked = writeLock();
     1061        try {
     1062            if (key == null || keys == null) return;
     1063            if (!hasKey(key))
     1064                return;
     1065            Map<String, String> originalKeys = getKeys();
     1066            if (keys.length == 2) {
     1067                keys = null;
     1068                keysChangedImpl(originalKeys);
     1069                return;
     1070            }
     1071            String[] newKeys = new String[keys.length - 2];
     1072            int j=0;
     1073            for (int i=0; i < keys.length; i+=2) {
     1074                if (!keys[i].equals(key)) {
     1075                    newKeys[j++] = keys[i];
     1076                    newKeys[j++] = keys[i+1];
     1077                }
     1078            }
    9721079            keys = newKeys;
    9731080            keysChangedImpl(originalKeys);
    974         }
    975     }
    976     /**
    977      * Remove the given key from the list
    978      *
    979      * @param key  the key to be removed. Ignored, if key is null.
    980      */
    981     public final void remove(String key) {
    982         if (key == null || keys == null) return;
    983         if (!hasKey(key))
    984             return;
    985         Map<String, String> originalKeys = getKeys();
    986         if (keys.length == 2) {
    987             keys = null;
    988             keysChangedImpl(originalKeys);
    989             return;
    990         }
    991         String[] newKeys = new String[keys.length - 2];
    992         int j=0;
    993         for (int i=0; i < keys.length; i+=2) {
    994             if (!keys[i].equals(key)) {
    995                 newKeys[j++] = keys[i];
    996                 newKeys[j++] = keys[i+1];
    997             }
    998         }
    999         keys = newKeys;
    1000         keysChangedImpl(originalKeys);
     1081        } finally {
     1082            writeUnlock(locked);
     1083        }
    10011084    }
    10021085
     
    10071090     */
    10081091    public final void removeAll() {
    1009         if (keys != null) {
    1010             Map<String, String> originalKeys = getKeys();
    1011             keys = null;
    1012             keysChangedImpl(originalKeys);
     1092        boolean locked = writeLock();
     1093        try {
     1094            if (keys != null) {
     1095                Map<String, String> originalKeys = getKeys();
     1096                keys = null;
     1097                keysChangedImpl(originalKeys);
     1098            }
     1099        } finally {
     1100            writeUnlock(locked);
    10131101        }
    10141102    }
     
    10221110     */
    10231111    public final String get(String key) {
     1112        String[] keys = this.keys;
    10241113        if (key == null)
    10251114            return null;
     
    10331122
    10341123    public final Collection<String> keySet() {
     1124        String[] keys = this.keys;
    10351125        if (keys == null)
    10361126            return Collections.emptySet();
     
    10681158     */
    10691159    public boolean hasKey(String key) {
     1160        String[] keys = this.keys;
    10701161        if (key == null) return false;
    10711162        if (keys == null) return false;
     
    11661257        // when way is cloned
    11671258        checkDataset();
     1259        Object referrers = this.referrers;
    11681260        List<OsmPrimitive> result = new ArrayList<OsmPrimitive>();
    11691261        if (referrers != null) {
     
    12021294     */
    12031295    public void cloneFrom(OsmPrimitive other) {
     1296        // write lock is provided by subclasses
    12041297        if (id != other.id && dataSet != null)
    12051298            throw new DataIntegrityProblemException("Osm id cannot be changed after primitive was added to the dataset");
     
    12401333     */
    12411334    public void mergeFrom(OsmPrimitive other) {
    1242         CheckParameterUtil.ensureParameterNotNull(other, "other");
    1243         if (other.isNew() ^ isNew())
    1244             throw new DataIntegrityProblemException(tr("Cannot merge because either of the participating primitives is new and the other is not"));
    1245         if (! other.isNew() && other.getId() != id)
    1246             throw new DataIntegrityProblemException(tr("Cannot merge primitives with different ids. This id is {0}, the other is {1}", id, other.getId()));
    1247 
    1248         setKeys(other.getKeys());
    1249         timestamp = other.timestamp;
    1250         version = other.version;
    1251         setIncomplete(other.isIncomplete());
    1252         flags = other.flags;
    1253         user= other.user;
    1254         changesetId = other.changesetId;
     1335        boolean locked = writeLock();
     1336        try {
     1337            CheckParameterUtil.ensureParameterNotNull(other, "other");
     1338            if (other.isNew() ^ isNew())
     1339                throw new DataIntegrityProblemException(tr("Cannot merge because either of the participating primitives is new and the other is not"));
     1340            if (! other.isNew() && other.getId() != id)
     1341                throw new DataIntegrityProblemException(tr("Cannot merge primitives with different ids. This id is {0}, the other is {1}", id, other.getId()));
     1342
     1343            setKeys(other.getKeys());
     1344            timestamp = other.timestamp;
     1345            version = other.version;
     1346            setIncomplete(other.isIncomplete());
     1347            flags = other.flags;
     1348            user= other.user;
     1349            changesetId = other.changesetId;
     1350        } finally {
     1351            writeUnlock(locked);
     1352        }
    12551353    }
    12561354
     
    13131411     */
    13141412    public String getName() {
    1315         if (get("name") != null)
    1316             return get("name");
    1317         return null;
     1413        return get("name");
    13181414    }
    13191415
     
    13561452     */
    13571453    public void load(PrimitiveData data) {
     1454        // Write lock is provided by subclasses
    13581455        setKeys(data.getKeys());
    13591456        setTimestamp(data.getTimestamp());
Note: See TracChangeset for help on using the changeset viewer.