--- 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'!