List.st
changeset 4706 19cb0f0edf69
parent 4697 1ac02e269555
child 4707 e0b53a001864
--- a/List.st	Mon Jul 30 12:14:26 2018 +0200
+++ b/List.st	Wed Aug 01 12:00:55 2018 +0200
@@ -16,7 +16,7 @@
 "{ NameSpace: Smalltalk }"
 
 OrderedCollection subclass:#List
-	instanceVariableNames:'dependents'
+	instanceVariableNames:'dependents optionalAccessLock'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'Collections-Sequenceable'
@@ -54,11 +54,16 @@
     It has been mostly provided, for ST-80 compatibility,
     where it adds sorting capabilities.
 
+    New: 
+        if the optional optionalAccessLock is set, all operations
+        are protected by a critical region.
+        (i.e. the List is a SynchronizedCollection)
+        
     [caveat:]
         'List' is probably a bad name, which may confuse beginners.
         I have nothing in common with LinkedLists.
         Instances are just regular ordered collections, with the added benefit of
-        sending out information about changes.
+        sending out information about changes, and an optional synchronization lock.
         Thus, they can be used as a model of textviews or selection list views,
         which need to redraw whenever the contents of the list changes.
         (and Lists not only send out change notifications when modified,
@@ -85,22 +90,24 @@
 
     |idx "{ Class: SmallInteger }"|
 
-    idx := anIndex + firstIndex - 1.
-    ((anIndex < 1) or:[idx > lastIndex]) ifTrue:[
-        idx == (lastIndex+1) ifTrue:[
-            self makeRoomAtLast.
-            lastIndex := lastIndex + 1.
-        ] ifFalse:[
-            ^ self subscriptBoundsError:anIndex
-        ]
+    self possiblySynchronized:[
+        idx := anIndex + firstIndex - 1.
+        ((anIndex < 1) or:[idx > lastIndex]) ifTrue:[
+            idx == (lastIndex+1) ifTrue:[
+                self makeRoomAtLast.
+                lastIndex := lastIndex + 1.
+            ] ifFalse:[
+                ^ self subscriptBoundsError:anIndex
+            ]
+        ].
+
+        contentsArray basicAt:idx put:anObject.
+        dependents notNil ifTrue:[self changed:#at: with:anIndex].
     ].
-    
-    contentsArray basicAt:idx put:anObject.
-    dependents notNil ifTrue:[self changed:#at: with:anIndex].
     ^ anObject
 
     "Modified: / 28-01-1998 / 16:44:49 / cg"
-    "Modified: / 20-06-2018 / 12:47:13 / Claus Gittinger"
+    "Modified: / 01-08-2018 / 11:46:40 / Claus Gittinger"
 !
 
 list
@@ -113,61 +120,84 @@
     "add the argument, anObject to the end of the collection
      Return the argument, anObject."
 
-    super add:anObject.
-    dependents notNil ifTrue:[self changed:#insert: with:(self size)].
+    self possiblySynchronized:[
+        super add:anObject.
+        dependents notNil ifTrue:[self changed:#insert: with:(self size)].
+    ].
     ^ anObject
 
-    "Modified: / 29.1.1998 / 10:52:32 / cg"
+    "Modified: / 29-01-1998 / 10:52:32 / cg"
+    "Modified: / 01-08-2018 / 11:46:58 / Claus Gittinger"
 !
 
 add:anObject beforeIndex:index
     "add the argument, anObject to the end of the collection.
      Return the receiver (sigh - ST-80 compatibility)."
 
-    super add:anObject beforeIndex:index.
-    dependents notNil ifTrue:[self changed:#insert: with:index].
+    self possiblySynchronized:[
+        super add:anObject beforeIndex:index.
+        dependents notNil ifTrue:[
+            self changed:#insert: with:index
+        ].
+    ].
+
+    "Modified (format): / 01-08-2018 / 11:47:24 / Claus Gittinger"
 !
 
 addAll:aCollection beforeIndex:index
     "insert all elements of the argument
      Return the receiver."
 
-    super addAll:aCollection beforeIndex:index.
-    dependents notNil ifTrue:[
-        self changed: #insertCollection: with:(Array with:index with:(aCollection size))
+    self possiblySynchronized:[
+        super addAll:aCollection beforeIndex:index.
+        dependents notNil ifTrue:[
+            self changed: #insertCollection: with:(Array with:index with:(aCollection size))
+        ]
     ]
+
+    "Modified: / 01-08-2018 / 11:47:17 / Claus Gittinger"
 !
 
 addAll:aCollection from:startIndex to:endIndex beforeIndex:index
     "insert elements start to stop from the argument
      Return the receiver."
 
-    super addAll:aCollection from:startIndex to:endIndex beforeIndex:index.
-    dependents notNil ifTrue:[
-        self changed: #insertCollection: with:(Array with:index with:(endIndex - startIndex + 1))
+    self possiblySynchronized:[
+        super addAll:aCollection from:startIndex to:endIndex beforeIndex:index.
+        dependents notNil ifTrue:[
+            self changed: #insertCollection: with:(Array with:index with:(endIndex - startIndex + 1))
+        ]
     ]
 
     "Modified: / 29-01-1998 / 10:52:57 / cg"
     "Modified: / 30-07-2018 / 11:16:31 / Stefan Vogel"
+    "Modified: / 01-08-2018 / 11:47:38 / Claus Gittinger"
 !
 
 addAllLast:aCollection
     "add all elements of the argument, aCollection to the end of the collection.
      Return the argument, aCollection."
 
-    self addAll:aCollection beforeIndex:self size + 1.
+    self possiblySynchronized:[
+        self addAll:aCollection beforeIndex:self size + 1.
+    ].    
     ^ aCollection
+
+    "Modified: / 01-08-2018 / 11:47:55 / Claus Gittinger"
 !
 
 addFirst:anObject
     "add the argument, anObject to the beginning of the collection.
      Return the argument, anObject."
 
-    super addFirst:anObject.
-    dependents notNil ifTrue:[self changed:#insert: with:1].
+    self possiblySynchronized:[
+        super addFirst:anObject.
+        dependents notNil ifTrue:[self changed:#insert: with:1].
+    ].
     ^ anObject
 
-    "Modified: / 29.1.1998 / 10:53:09 / cg"
+    "Modified: / 29-01-1998 / 10:53:09 / cg"
+    "Modified: / 01-08-2018 / 11:48:05 / Claus Gittinger"
 !
 
 clearContents
@@ -176,34 +206,41 @@
      to a size which is similar to the lists current size.
      Returns the receiver."
 
-     |prevSize|
+    self possiblySynchronized:[
+        |prevSize|
 
-     prevSize := self size.
-     super clearContents.
+        prevSize := self size.
+        super clearContents.
 
-     prevSize ~~ 0 ifTrue:[
         dependents notNil ifTrue:[
-            self changed:#removeFrom: with:(Array with:1 with:prevSize)
+            prevSize ~~ 0 ifTrue:[
+                self changed:#removeFrom: with:(Array with:1 with:prevSize)
+            ]
         ]
      ]
+
+    "Modified (format): / 01-08-2018 / 11:51:29 / Claus Gittinger"
 !
 
 removeAll
     "remove all elements from the collection.
      Returns the receiver."
 
-     |prevSize|
+    self possiblySynchronized:[
+        |prevSize|
 
-     prevSize := self size.
-     super removeAll.
+        prevSize := self size.
+        super removeAll.
 
-     prevSize ~~ 0 ifTrue:[
         dependents notNil ifTrue:[
-            self changed:#removeFrom: with:(Array with:1 with:prevSize)
+            prevSize ~~ 0 ifTrue:[
+               self changed:#removeFrom: with:(Array with:1 with:prevSize)
+           ]
         ]
-     ]
+    ]
 
-    "Modified: / 29.1.1998 / 10:53:28 / cg"
+    "Modified: / 29-01-1998 / 10:53:28 / cg"
+    "Modified (format): / 01-08-2018 / 11:51:21 / Claus Gittinger"
 !
 
 removeAllSuchThat:aBlock
@@ -215,23 +252,30 @@
 
     |removedElements|
 
-    removedElements := super removeAllSuchThat:aBlock.
-    removedElements notEmpty ifTrue:[
-        self changed.
+    self possiblySynchronized:[
+        removedElements := super removeAllSuchThat:aBlock.
+        removedElements notEmpty ifTrue:[
+            self changed.
+        ].
     ].
     ^ removedElements.
+
+    "Modified: / 01-08-2018 / 11:49:44 / Claus Gittinger"
 !
 
 removeFirst
     "remove the first element from the collection; return the element."
 
-     |deletedObject|
+    |deletedObject|
 
-     deletedObject := super removeFirst.
-     dependents notNil ifTrue:[self changed:#remove: with:1].
-     ^ deletedObject
+    self possiblySynchronized:[
+        deletedObject := super removeFirst.
+        dependents notNil ifTrue:[self changed:#remove: with:1].
+    ].
+    ^ deletedObject
 
-    "Modified: / 29.1.1998 / 10:53:36 / cg"
+    "Modified: / 29-01-1998 / 10:53:36 / cg"
+    "Modified: / 01-08-2018 / 11:49:58 / Claus Gittinger"
 !
 
 removeFirst:n
@@ -240,11 +284,14 @@
 
     |deletedObjects|
 
-    deletedObjects := super removeFirst:n.
-    dependents notNil ifTrue:[self changed:#removeFrom: with:(Array with:1 with:n)].
+    self possiblySynchronized:[
+        deletedObjects := super removeFirst:n.
+        dependents notNil ifTrue:[self changed:#removeFrom: with:(Array with:1 with:n)].
+    ].
     ^ deletedObjects
 
-    "Modified: / 29.1.1998 / 10:53:40 / cg"
+    "Modified: / 29-01-1998 / 10:53:40 / cg"
+    "Modified: / 01-08-2018 / 11:50:10 / Claus Gittinger"
 !
 
 removeFirstIfAbsent:exceptionBlock
@@ -252,11 +299,14 @@
      If there is no element in the receiver collection, return the value from
      exceptionBlock."
 
-    self notEmpty ifTrue:[ ^ self removeFirst ].
+    self possiblySynchronized:[
+        self notEmpty ifTrue:[ ^ self removeFirst ].
+    ].
     ^ exceptionBlock value
 
     "Modified: / 21-10-2006 / 23:03:46 / cg"
     "Modified: / 11-04-2018 / 11:52:30 / stefan"
+    "Modified: / 01-08-2018 / 11:50:26 / Claus Gittinger"
 !
 
 removeFromIndex:startIndex toIndex:stopIndex
@@ -269,13 +319,16 @@
 
     stopIndex < startIndex ifTrue:[^ self].
 
-    ret := super removeFromIndex:startIndex toIndex:stopIndex.
-    dependents notNil ifTrue:[
-        self changed:#removeFrom: with:(Array with:startIndex with:stopIndex).
-    ].
+    self possiblySynchronized:[
+        ret := super removeFromIndex:startIndex toIndex:stopIndex.
+        dependents notNil ifTrue:[
+            self changed:#removeFrom: with:(Array with:startIndex with:stopIndex).
+        ].
+    ].    
     ^ ret
 
-    "Modified: / 29.1.1998 / 10:54:03 / cg"
+    "Modified: / 29-01-1998 / 10:54:03 / cg"
+    "Modified: / 01-08-2018 / 11:50:42 / Claus Gittinger"
 !
 
 removeIdentical:anObject ifAbsent:exceptionBlock
@@ -284,43 +337,53 @@
      if not, return the value from evaluating exceptionBlock.
      Uses identity compare (==) to search for the element."
 
-    |index|
+    self possiblySynchronized:[
+        |index|
 
-    index := self identityIndexOf:anObject.
+        index := self identityIndexOf:anObject.
 
-    index == 0 ifTrue:[ ^ exceptionBlock value ].
-    self removeFromIndex:index toIndex:index.
+        index == 0 ifTrue:[ ^ exceptionBlock value ].
+        self removeFromIndex:index toIndex:index.
+    ].    
     ^ anObject
 
     "Modified: / 21-10-2006 / 23:03:29 / cg"
+    "Modified (format): / 01-08-2018 / 11:51:07 / Claus Gittinger"
 !
 
 removeLast
     "remove the last element from the collection; return the element"
 
-     |deletedObject|
+    |deletedObject|
 
-     deletedObject :=  super removeLast.
-     dependents notNil ifTrue:[self changed:#remove: with:(1 + self size)].
-     ^ deletedObject
+    self possiblySynchronized:[
+        deletedObject :=  super removeLast.
+        dependents notNil ifTrue:[self changed:#remove: with:(1 + self size)].
+    ].
+    ^ deletedObject
 
-    "Modified: / 29.1.1998 / 10:54:15 / cg"
+    "Modified: / 29-01-1998 / 10:54:15 / cg"
+    "Modified: / 01-08-2018 / 11:51:51 / Claus Gittinger"
 !
 
 removeLast:n
     "remove the last n elements from the receiver collection. 
      Return a collection of removed elements."
 
-    |deletedObjects stop|
+    |deletedObjects|
 
-    stop := self size.
-    deletedObjects := super removeLast:n.
-    dependents notNil ifTrue:[
-        self changed:#removeFrom: with:(Array with:(stop - n + 1) with:stop).
+    self possiblySynchronized:[
+        | stop |
+        stop := self size.
+        deletedObjects := super removeLast:n.
+        dependents notNil ifTrue:[
+            self changed:#removeFrom: with:(Array with:(stop - n + 1) with:stop).
+        ].
     ].
     ^ deletedObjects
 
-    "Modified: / 29.1.1998 / 10:54:25 / cg"
+    "Modified: / 29-01-1998 / 10:54:25 / cg"
+    "Modified: / 01-08-2018 / 11:52:10 / Claus Gittinger"
 !
 
 removeLastIfAbsent:exceptionBlock
@@ -328,11 +391,14 @@
      If there is no element in the receiver collection, return the value from
      exceptionBlock."
 
-    self notEmpty ifTrue:[ ^ self removeLast ].
+    self possiblySynchronized:[
+        self notEmpty ifTrue:[ ^ self removeLast ].
+    ].    
     ^ exceptionBlock value
 
     "Modified: / 21-10-2006 / 23:03:53 / cg"
     "Modified: / 30-07-2018 / 11:12:56 / Stefan Vogel"
+    "Modified: / 01-08-2018 / 11:52:25 / Claus Gittinger"
 !
 
 reset
@@ -340,16 +406,20 @@
      That's almost the same as #removeAll, but keeps the contentsArray.
      Returns the receiver."
 
-     |prevSize|
+    self possiblySynchronized:[
+        |prevSize|
 
-     prevSize := self size.
-     super reset.
+        prevSize := self size.
+        super reset.
 
-     prevSize ~~ 0 ifTrue:[
         dependents notNil ifTrue:[
-            self changed:#removeFrom: with:(Array with:1 with:prevSize)
-        ]
-     ].
+            prevSize ~~ 0 ifTrue:[
+               self changed:#removeFrom: with:(Array with:1 with:prevSize)
+           ]
+        ].
+    ].
+
+    "Modified: / 01-08-2018 / 11:52:46 / Claus Gittinger"
 ! !
 
 !List methodsFor:'converting'!
@@ -358,6 +428,20 @@
     ^ self
 
     "Created: 14.2.1997 / 16:25:55 / cg"
+!
+
+asSharedCollection
+    |newList|
+    
+    optionalAccessLock notNil ifTrue:[^ self].
+    
+    self possiblySynchronized:[
+        newList := List withAll:self.
+        newList beSynchronized.
+    ].    
+    ^ newList
+
+    "Created: / 01-08-2018 / 11:54:26 / Claus Gittinger"
 ! !
 
 !List methodsFor:'copying'!
@@ -430,35 +514,37 @@
     "replace all elements in the receiver by aCollection,
      Redefined - can be done faster"
 
-     |oldSize newSize|
+    |oldSize newSize|
 
-     aCollection isSequenceable ifFalse:[
-        ^ super contents:aCollection
-     ].
+    aCollection isSequenceable ifFalse:[
+       ^ super contents:aCollection
+    ].
 
-     oldSize := self size.
-     newSize := aCollection size.
+    self possiblySynchronized:[
+        oldSize := self size.
+        newSize := aCollection size.
 
-     newSize < oldSize ifTrue:[
-        self replaceFrom:1 to:newSize with:aCollection startingAt:1.
-        self removeFromIndex:newSize+1 toIndex:oldSize.
-     ] ifFalse:[
-        newSize > oldSize ifTrue:[
-            oldSize == 0 ifTrue:[
-                self addAll:aCollection       
-            ] ifFalse:[
-                self replaceFrom:1 to:oldSize with:aCollection startingAt:1.
-                self addAll:aCollection from:oldSize+1 to:newSize beforeIndex:oldSize+1        
-            ]
+        newSize < oldSize ifTrue:[
+           self replaceFrom:1 to:newSize with:aCollection startingAt:1.
+           self removeFromIndex:newSize+1 toIndex:oldSize.
         ] ifFalse:[
-            "/ same size
-            oldSize ~~ 0 ifTrue:[
-                self replaceFrom:1 to:newSize with:aCollection startingAt:1.
-            ]
-        ]
-     ].
-
-     "
+           newSize > oldSize ifTrue:[
+               oldSize == 0 ifTrue:[
+                   self addAll:aCollection       
+               ] ifFalse:[
+                   self replaceFrom:1 to:oldSize with:aCollection startingAt:1.
+                   self addAll:aCollection from:oldSize+1 to:newSize beforeIndex:oldSize+1        
+               ]
+           ] ifFalse:[
+               "/ same size
+               oldSize ~~ 0 ifTrue:[
+                   self replaceFrom:1 to:newSize with:aCollection startingAt:1.
+               ]
+           ]
+        ].
+    ].
+    
+    "
       |l|
       l := List new.
       l contents:#(1 2 3 4 5).
@@ -481,7 +567,9 @@
       l addAll:#(1 2 3 4 5).
       l contents:#(10 20 30 40 50).
       l      
-     "
+    "
+
+    "Modified: / 01-08-2018 / 11:55:20 / Claus Gittinger"
 !
 
 list:aCollection
@@ -524,31 +612,61 @@
 
     stop < start ifTrue:[^ self].
 
-    "/ see if there is really any change involved
-    (self sameContentsFrom:start to:stop as:aCollection startingAt:repStart) ifTrue:[
-        ^ self  "/ avoids useless change notifications
+    self possiblySynchronized:[
+        "/ see if there is really any change involved
+        (self sameContentsFrom:start to:stop as:aCollection startingAt:repStart) ifTrue:[
+            ^ self  "/ avoids useless change notifications
+        ].
+
+        super replaceFrom:start to:stop with:aCollection startingAt:repStart.
+
+        dependents notNil ifTrue:[
+            self changed:#replace: with:(Array with:start with:stop)
+        ].
     ].
 
-    super replaceFrom:start to:stop with:aCollection startingAt:repStart.
-
-    dependents notNil ifTrue:[
-        self changed:#replace: with:(Array with:start with:stop)
-    ].
-
-    "Modified: / 20.5.1998 / 15:20:17 / cg"
+    "Modified: / 20-05-1998 / 15:20:17 / cg"
+    "Modified: / 01-08-2018 / 11:55:41 / Claus Gittinger"
 !
 
 setContents:aCollection
     "replace the receiver's underlying collection by aCollection"
 
-     aCollection isSequenceable ifFalse:[
-        ^ super contents:aCollection
-     ].
+    self possiblySynchronized:[
+        aCollection isSequenceable ifFalse:[
+           ^ super contents:aCollection
+        ].
+
+        contentsArray := aCollection.
+        firstIndex := 1.
+        lastIndex := aCollection size.
+        self changed.
+    ].
+
+    "Modified: / 01-08-2018 / 11:56:01 / Claus Gittinger"
+! !
+
+!List methodsFor:'private'!
 
-     contentsArray := aCollection.
-     firstIndex := 1.
-     lastIndex := aCollection size.
-     self changed.
+possiblySynchronized:aBlock
+    optionalAccessLock notNil ifTrue:[
+        ^ optionalAccessLock synchronized:aBlock.
+    ].
+    ^ aBlock value.
+
+    "Created: / 01-08-2018 / 11:46:01 / Claus Gittinger"
+! !
+
+!List methodsFor:'setup'!
+
+beSynchronized
+    "make the receiver a synchronized List"
+    
+    optionalAccessLock isNil ifTrue:[
+        optionalAccessLock := RecursionLock new
+    ].
+
+    "Created: / 01-08-2018 / 11:44:37 / Claus Gittinger"
 ! !
 
 !List methodsFor:'testing'!