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/DataSet.java

    r3316 r3348  
    1515import java.util.List;
    1616import java.util.Map;
     17import java.util.concurrent.locks.Lock;
     18import java.util.concurrent.locks.ReadWriteLock;
     19import java.util.concurrent.locks.ReentrantReadWriteLock;
    1720
    1821import org.openstreetmap.josm.data.SelectionChangedListener;
     
    4245public class DataSet implements Cloneable {
    4346
     47    /**
     48     * Maximum number of events that can be fired between beginUpdate/endUpdate to be send as single events (ie without DatasetChangedEvent)
     49     */
     50    private static final int MAX_SINGLE_EVENTS = 30;
     51
     52    /**
     53     * Maximum number of events to kept between beginUpdate/endUpdate. When more events are created, that simple DatasetChangedEvent is sent)
     54     */
     55    private static final int MAX_EVENTS = 1000;
     56
    4457    private static class IdHash implements Hash<PrimitiveId,OsmPrimitive> {
    4558
     
    5770    private Map<PrimitiveId, OsmPrimitive> primitivesMap = allPrimitives.foreignKey(new IdHash());
    5871    private List<DataSetListener> listeners = new ArrayList<DataSetListener>();
     72
    5973    // Number of open calls to beginUpdate
    6074    private int updateCount;
     75    // Events that occurred while dataset was locked but should be fired after write lock is released
     76    private final List<AbstractDatasetChangedEvent> cachedEvents = new ArrayList<AbstractDatasetChangedEvent>();
    6177
    6278    private int highlightUpdateCount;
     79
     80    private final ReadWriteLock lock = new ReentrantReadWriteLock();
     81
     82    public Lock getReadLock() {
     83        return lock.readLock();
     84    }
    6385
    6486    /**
     
    212234     */
    213235    public void addPrimitive(OsmPrimitive primitive) {
    214         if (getPrimitiveById(primitive) != null)
    215             throw new DataIntegrityProblemException(
    216                     tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString()));
    217 
    218         primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reinexRelation to work properly)
    219         if (primitive instanceof Node) {
    220             nodes.add((Node) primitive);
    221         } else if (primitive instanceof Way) {
    222             ways.add((Way) primitive);
    223         } else if (primitive instanceof Relation) {
    224             relations.add((Relation) primitive);
    225         }
    226         allPrimitives.add(primitive);
    227         primitive.setDataset(this);
    228         firePrimitivesAdded(Collections.singletonList(primitive), false);
     236        beginUpdate();
     237        try {
     238            if (getPrimitiveById(primitive) != null)
     239                throw new DataIntegrityProblemException(
     240                        tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString()));
     241
     242            primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reinexRelation to work properly)
     243            if (primitive instanceof Node) {
     244                nodes.add((Node) primitive);
     245            } else if (primitive instanceof Way) {
     246                ways.add((Way) primitive);
     247            } else if (primitive instanceof Relation) {
     248                relations.add((Relation) primitive);
     249            }
     250            allPrimitives.add(primitive);
     251            primitive.setDataset(this);
     252            firePrimitivesAdded(Collections.singletonList(primitive), false);
     253        } finally {
     254            endUpdate();
     255        }
    229256    }
    230257
    231258    public OsmPrimitive addPrimitive(PrimitiveData data) {
    232         OsmPrimitive result;
    233         if (data instanceof NodeData) {
    234             result = new Node();
    235         } else if (data instanceof WayData) {
    236             result = new Way();
    237         } else if (data instanceof RelationData) {
    238             result = new Relation();
    239         } else
    240             throw new AssertionError();
    241         result.setDataset(this);
    242         result.load(data);
    243         addPrimitive(result);
    244         return result;
     259        beginUpdate();
     260        try {
     261            OsmPrimitive result;
     262            if (data instanceof NodeData) {
     263                result = new Node();
     264            } else if (data instanceof WayData) {
     265                result = new Way();
     266            } else if (data instanceof RelationData) {
     267                result = new Relation();
     268            } else
     269                throw new AssertionError();
     270            result.setDataset(this);
     271            result.load(data);
     272            addPrimitive(result);
     273            return result;
     274        } finally {
     275            endUpdate();
     276        }
    245277    }
    246278
     
    255287     */
    256288    public void removePrimitive(PrimitiveId primitiveId) {
    257         OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
    258         if (primitive == null)
    259             return;
    260         if (primitive instanceof Node) {
    261             nodes.remove(primitive);
    262         } else if (primitive instanceof Way) {
    263             ways.remove(primitive);
    264         } else if (primitive instanceof Relation) {
    265             relations.remove(primitive);
    266         }
    267         selectedPrimitives.remove(primitive);
    268         allPrimitives.remove(primitive);
    269         primitive.setDataset(null);
    270         firePrimitivesRemoved(Collections.singletonList(primitive), false);
     289        beginUpdate();
     290        try {
     291            OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
     292            if (primitive == null)
     293                return;
     294            if (primitive instanceof Node) {
     295                nodes.remove(primitive);
     296            } else if (primitive instanceof Way) {
     297                ways.remove(primitive);
     298            } else if (primitive instanceof Relation) {
     299                relations.remove(primitive);
     300            }
     301            selectedPrimitives.remove(primitive);
     302            allPrimitives.remove(primitive);
     303            primitive.setDataset(null);
     304            firePrimitivesRemoved(Collections.singletonList(primitive), false);
     305        } finally {
     306            endUpdate();
     307        }
    271308    }
    272309
     
    492529     */
    493530
    494 //    public void setDisabled(OsmPrimitive... osm) {
    495 //        if (osm.length == 1 && osm[0] == null) {
    496 //            setDisabled();
    497 //            return;
    498 //        }
    499 //        clearDisabled(allPrimitives());
    500 //        for (OsmPrimitive o : osm)
    501 //            if (o != null) {
    502 //                o.setDisabled(true);
    503 //            }
    504 //    }
    505 //
    506 //    public void setDisabled(Collection<? extends OsmPrimitive> selection) {
    507 //        clearDisabled(nodes);
    508 //        clearDisabled(ways);
    509 //        clearDisabled(relations);
    510 //        for (OsmPrimitive osm : selection) {
    511 //            osm.setDisabled(true);
    512 //        }
    513 //    }
    514 //
    515 //    /**
    516 //     * Remove the disabled parameter from every value in the collection.
    517 //     * @param list The collection to remove the disabled parameter from.
    518 //     */
    519 //    private void clearDisabled(Collection<? extends OsmPrimitive> list) {
    520 //        for (OsmPrimitive osm : list) {
    521 //            osm.setDisabled(false);
    522 //        }
    523 //    }
    524 //
    525 //
    526 //    public void setFiltered(Collection<? extends OsmPrimitive> selection) {
    527 //        clearFiltered(nodes);
    528 //        clearFiltered(ways);
    529 //        clearFiltered(relations);
    530 //        for (OsmPrimitive osm : selection) {
    531 //            osm.setFiltered(true);
    532 //        }
    533 //    }
    534 //
    535 //    public void setFiltered(OsmPrimitive... osm) {
    536 //        if (osm.length == 1 && osm[0] == null) {
    537 //            setFiltered();
    538 //            return;
    539 //        }
    540 //        clearFiltered(nodes);
    541 //        clearFiltered(ways);
    542 //        clearFiltered(relations);
    543 //        for (OsmPrimitive o : osm)
    544 //            if (o != null) {
    545 //                o.setFiltered(true);
    546 //            }
    547 //    }
    548 //
    549 //    /**
    550 //     * Remove the filtered parameter from every value in the collection.
    551 //     * @param list The collection to remove the filtered parameter from.
    552 //     */
    553 //    private void clearFiltered(Collection<? extends OsmPrimitive> list) {
    554 //        if (list == null)
    555 //            return;
    556 //        for (OsmPrimitive osm : list) {
    557 //            osm.setFiltered(false);
    558 //        }
    559 //    }
     531    //    public void setDisabled(OsmPrimitive... osm) {
     532    //        if (osm.length == 1 && osm[0] == null) {
     533    //            setDisabled();
     534    //            return;
     535    //        }
     536    //        clearDisabled(allPrimitives());
     537    //        for (OsmPrimitive o : osm)
     538    //            if (o != null) {
     539    //                o.setDisabled(true);
     540    //            }
     541    //    }
     542    //
     543    //    public void setDisabled(Collection<? extends OsmPrimitive> selection) {
     544    //        clearDisabled(nodes);
     545    //        clearDisabled(ways);
     546    //        clearDisabled(relations);
     547    //        for (OsmPrimitive osm : selection) {
     548    //            osm.setDisabled(true);
     549    //        }
     550    //    }
     551    //
     552    //    /**
     553    //     * Remove the disabled parameter from every value in the collection.
     554    //     * @param list The collection to remove the disabled parameter from.
     555    //     */
     556    //    private void clearDisabled(Collection<? extends OsmPrimitive> list) {
     557    //        for (OsmPrimitive osm : list) {
     558    //            osm.setDisabled(false);
     559    //        }
     560    //    }
     561    //
     562    //
     563    //    public void setFiltered(Collection<? extends OsmPrimitive> selection) {
     564    //        clearFiltered(nodes);
     565    //        clearFiltered(ways);
     566    //        clearFiltered(relations);
     567    //        for (OsmPrimitive osm : selection) {
     568    //            osm.setFiltered(true);
     569    //        }
     570    //    }
     571    //
     572    //    public void setFiltered(OsmPrimitive... osm) {
     573    //        if (osm.length == 1 && osm[0] == null) {
     574    //            setFiltered();
     575    //            return;
     576    //        }
     577    //        clearFiltered(nodes);
     578    //        clearFiltered(ways);
     579    //        clearFiltered(relations);
     580    //        for (OsmPrimitive o : osm)
     581    //            if (o != null) {
     582    //                o.setFiltered(true);
     583    //            }
     584    //    }
     585    //
     586    //    /**
     587    //     * Remove the filtered parameter from every value in the collection.
     588    //     * @param list The collection to remove the filtered parameter from.
     589    //     */
     590    //    private void clearFiltered(Collection<? extends OsmPrimitive> list) {
     591    //        if (list == null)
     592    //            return;
     593    //        for (OsmPrimitive osm : list) {
     594    //            osm.setFiltered(false);
     595    //        }
     596    //    }
    560597
    561598    @Override public DataSet clone() {
    562         DataSet ds = new DataSet();
    563         HashMap<OsmPrimitive, OsmPrimitive> primitivesMap = new HashMap<OsmPrimitive, OsmPrimitive>();
    564         for (Node n : nodes) {
    565             Node newNode = new Node(n);
    566             primitivesMap.put(n, newNode);
    567             ds.addPrimitive(newNode);
    568         }
    569         for (Way w : ways) {
    570             Way newWay = new Way(w);
    571             primitivesMap.put(w, newWay);
    572             List<Node> newNodes = new ArrayList<Node>();
    573             for (Node n: w.getNodes()) {
    574                 newNodes.add((Node)primitivesMap.get(n));
    575             }
    576             newWay.setNodes(newNodes);
    577             ds.addPrimitive(newWay);
    578         }
    579         // Because relations can have other relations as members we first clone all relations
    580         // and then get the cloned members
    581         for (Relation r : relations) {
    582             Relation newRelation = new Relation(r, r.isNew());
    583             newRelation.setMembers(null);
    584             primitivesMap.put(r, newRelation);
    585             ds.addPrimitive(newRelation);
    586         }
    587         for (Relation r : relations) {
    588             Relation newRelation = (Relation)primitivesMap.get(r);
    589             List<RelationMember> newMembers = new ArrayList<RelationMember>();
    590             for (RelationMember rm: r.getMembers()) {
    591                 newMembers.add(new RelationMember(rm.getRole(), primitivesMap.get(rm.getMember())));
    592             }
    593             newRelation.setMembers(newMembers);
    594         }
    595         for (DataSource source : dataSources) {
    596             ds.dataSources.add(new DataSource(source.bounds, source.origin));
    597         }
    598         ds.version = version;
    599         return ds;
     599        getReadLock().lock();
     600        try {
     601            DataSet ds = new DataSet();
     602            HashMap<OsmPrimitive, OsmPrimitive> primitivesMap = new HashMap<OsmPrimitive, OsmPrimitive>();
     603            for (Node n : nodes) {
     604                Node newNode = new Node(n);
     605                primitivesMap.put(n, newNode);
     606                ds.addPrimitive(newNode);
     607            }
     608            for (Way w : ways) {
     609                Way newWay = new Way(w);
     610                primitivesMap.put(w, newWay);
     611                List<Node> newNodes = new ArrayList<Node>();
     612                for (Node n: w.getNodes()) {
     613                    newNodes.add((Node)primitivesMap.get(n));
     614                }
     615                newWay.setNodes(newNodes);
     616                ds.addPrimitive(newWay);
     617            }
     618            // Because relations can have other relations as members we first clone all relations
     619            // and then get the cloned members
     620            for (Relation r : relations) {
     621                Relation newRelation = new Relation(r, r.isNew());
     622                newRelation.setMembers(null);
     623                primitivesMap.put(r, newRelation);
     624                ds.addPrimitive(newRelation);
     625            }
     626            for (Relation r : relations) {
     627                Relation newRelation = (Relation)primitivesMap.get(r);
     628                List<RelationMember> newMembers = new ArrayList<RelationMember>();
     629                for (RelationMember rm: r.getMembers()) {
     630                    newMembers.add(new RelationMember(rm.getRole(), primitivesMap.get(rm.getMember())));
     631                }
     632                newRelation.setMembers(newMembers);
     633            }
     634            for (DataSource source : dataSources) {
     635                ds.dataSources.add(new DataSource(source.bounds, source.origin));
     636            }
     637            ds.version = version;
     638            return ds;
     639        } finally {
     640            getReadLock().unlock();
     641        }
    600642    }
    601643
     
    663705    }
    664706
    665     protected void deleteWay(Way way) {
     707    private void deleteWay(Way way) {
    666708        way.setNodes(null);
    667709        way.setDeleted(true);
     
    674716     */
    675717    public void unlinkNodeFromWays(Node node) {
    676         for (Way way: ways) {
    677             List<Node> nodes = way.getNodes();
    678             if (nodes.remove(node)) {
    679                 if (nodes.size() < 2) {
    680                     deleteWay(way);
    681                 } else {
    682                     way.setNodes(nodes);
     718        beginUpdate();
     719        try {
     720            for (Way way: ways) {
     721                List<Node> nodes = way.getNodes();
     722                if (nodes.remove(node)) {
     723                    if (nodes.size() < 2) {
     724                        deleteWay(way);
     725                    } else {
     726                        way.setNodes(nodes);
     727                    }
    683728                }
    684729            }
     730        } finally {
     731            endUpdate();
    685732        }
    686733    }
     
    692739     */
    693740    public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
    694         for (Relation relation : relations) {
    695             List<RelationMember> members = relation.getMembers();
    696 
    697             Iterator<RelationMember> it = members.iterator();
    698             boolean removed = false;
    699             while(it.hasNext()) {
    700                 RelationMember member = it.next();
    701                 if (member.getMember().equals(primitive)) {
    702                     it.remove();
    703                     removed = true;
     741        beginUpdate();
     742        try {
     743            for (Relation relation : relations) {
     744                List<RelationMember> members = relation.getMembers();
     745
     746                Iterator<RelationMember> it = members.iterator();
     747                boolean removed = false;
     748                while(it.hasNext()) {
     749                    RelationMember member = it.next();
     750                    if (member.getMember().equals(primitive)) {
     751                        it.remove();
     752                        removed = true;
     753                    }
    704754                }
    705             }
    706 
    707             if (removed) {
    708                 relation.setMembers(members);
    709             }
     755
     756                if (removed) {
     757                    relation.setMembers(members);
     758                }
     759            }
     760        } finally {
     761            endUpdate();
    710762        }
    711763    }
     
    718770     */
    719771    public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
    720         if (referencedPrimitive instanceof Node) {
    721             unlinkNodeFromWays((Node)referencedPrimitive);
    722             unlinkPrimitiveFromRelations(referencedPrimitive);
    723         } else {
    724             unlinkPrimitiveFromRelations(referencedPrimitive);
     772        beginUpdate();
     773        try {
     774            if (referencedPrimitive instanceof Node) {
     775                unlinkNodeFromWays((Node)referencedPrimitive);
     776                unlinkPrimitiveFromRelations(referencedPrimitive);
     777            } else {
     778                unlinkPrimitiveFromRelations(referencedPrimitive);
     779            }
     780        } finally {
     781            endUpdate();
    725782        }
    726783    }
     
    804861     */
    805862    public void beginUpdate() {
     863        lock.writeLock().lock();
    806864        updateCount++;
    807865    }
     
    814872            updateCount--;
    815873            if (updateCount == 0) {
    816                 fireDataChanged();
    817             }
     874                List<AbstractDatasetChangedEvent> eventsCopy = new ArrayList<AbstractDatasetChangedEvent>(cachedEvents);
     875                cachedEvents.clear();
     876                lock.writeLock().unlock();
     877
     878                if (!eventsCopy.isEmpty()) {
     879                    lock.readLock().lock();
     880                    try {
     881                        if (eventsCopy.size() < MAX_SINGLE_EVENTS) {
     882                            for (AbstractDatasetChangedEvent event: eventsCopy) {
     883                                fireEventToListeners(event);
     884                            }
     885                        } else if (eventsCopy.size() == MAX_EVENTS) {
     886                            fireEventToListeners(new DataChangedEvent(this));
     887                        } else {
     888                            fireEventToListeners(new DataChangedEvent(this, eventsCopy));
     889                        }
     890                    } finally {
     891                        lock.readLock().unlock();
     892                    }
     893                }
     894            } else {
     895                lock.writeLock().unlock();
     896            }
     897
    818898        } else
    819899            throw new AssertionError("endUpdate called without beginUpdate");
    820900    }
    821901
     902    private void fireEventToListeners(AbstractDatasetChangedEvent event) {
     903        for (DataSetListener listener: listeners) {
     904            event.fire(listener);
     905        }
     906    }
     907
    822908    private void fireEvent(AbstractDatasetChangedEvent event) {
    823         if (updateCount == 0) {
    824             for (DataSetListener dsl : listeners) {
    825                 event.fire(dsl);
    826             }
    827         }
    828     }
    829 
    830     private void fireDataChanged() {
    831         fireEvent(new DataChangedEvent(this));
     909        if (updateCount == 0)
     910            throw new AssertionError("dataset events can be fired only when dataset is locked");
     911        if (cachedEvents.size() < MAX_EVENTS) {
     912            cachedEvents.add(event);
     913        }
    832914    }
    833915
     
    868950
    869951    public void clenupDeletedPrimitives() {
    870         if (cleanupDeleted(nodes.iterator())
    871                 | cleanupDeleted(ways.iterator())
    872                 | cleanupDeleted(relations.iterator())) {
    873             fireSelectionChanged();
     952        beginUpdate();
     953        try {
     954            if (cleanupDeleted(nodes.iterator())
     955                    | cleanupDeleted(ways.iterator())
     956                    | cleanupDeleted(relations.iterator())) {
     957                fireSelectionChanged();
     958            }
     959        } finally {
     960            endUpdate();
    874961        }
    875962    }
     
    896983     */
    897984    public void clear() {
    898         clearSelection();
    899         for (OsmPrimitive primitive:allPrimitives) {
    900             primitive.setDataset(null);
    901         }
    902         nodes.clear();
    903         ways.clear();
    904         relations.clear();
    905         allPrimitives.clear();
     985        beginUpdate();
     986        try {
     987            clearSelection();
     988            for (OsmPrimitive primitive:allPrimitives) {
     989                primitive.setDataset(null);
     990            }
     991            nodes.clear();
     992            ways.clear();
     993            relations.clear();
     994            allPrimitives.clear();
     995        } finally {
     996            endUpdate();
     997        }
    906998    }
    907999}
Note: See TracChangeset for help on using the changeset viewer.