Levente Uzonyi uploaded a new version of KernelTests to project The Trunk:
http://source.squeak.org/trunk/KernelTests-ul.332.mcz
==================== Summary ====================
Name: KernelTests-ul.332
Author: ul
Time: 5 February 2018, 8:42:17.266896 pm
UUID: 93605545-3a6c-4059-9763-f8d733f1c218
Ancestors: KernelTests-tonyg.331
- added a test for object pinning
- use the #excessSignals accessor in SemaphoreTest
=============== Diff against KernelTests-tonyg.331 ===============
Item was added:
+ ----- Method: ObjectTest>>testPinning (in category 'tests') -----
+ testPinning
+ "Test pinning state changes for two objects. Iterate over all possible state transitions."
+
+ | objects |
+ objects := { Object new. Object new }.
+ #((false false) (false true) (true false) (true true))
+ combinations: 2
+ atATimeDo: [ :transition |
+ | fromState toState |
+ fromState := transition first.
+ toState := transition second.
+ fromState with: objects do: [ :pinned :object |
+ pinned
+ ifTrue: [ object pin ]
+ ifFalse: [ object unpin ].
+ self assert: pinned equals: object isPinned ].
+ objects withIndexDo: [ :object :index |
+ | from to |
+ from := fromState at: index.
+ to := toState at: index.
+ self assert: from equals: (to
+ ifTrue: [ object pin ]
+ ifFalse: [ object unpin ]).
+ self assert: to equals: object isPinned ] ]!
Item was changed:
----- Method: SemaphoreTest>>testSemaAfterCriticalWait (in category 'testing') -----
testSemaAfterCriticalWait "self run: #testSemaAfterCriticalWait"
"This tests whether a semaphore that has just left the wait in Semaphore>>critical:
leaves it with signaling the associated semaphore."
| s p |
s := Semaphore new.
p := [s critical:[]] forkAt: Processor activePriority-1.
"wait until p entered the critical section"
[p suspendingList == s] whileFalse:[(Delay forMilliseconds: 10) wait].
"Now that p entered it, signal the semaphore. p now 'owns' the semaphore
but since we are running at higher priority than p it will not get to do
anything."
s signal.
p terminate.
+ self assert: 1 equals: s excessSignals!
- self assert: ((s instVarNamed: #excessSignals) = 1)!
Item was changed:
----- Method: SemaphoreTest>>testSemaInCriticalWait (in category 'testing') -----
testSemaInCriticalWait "self run: #testSemaInCriticalWait"
"This tests whether a semaphore that has entered the wait in Semaphore>>critical:
leaves it without signaling the associated semaphore."
| s p |
s := Semaphore new.
p := [s critical:[]] fork.
Processor yield.
self assert:(p suspendingList == s).
p terminate.
+ self assert: 0 equals: s excessSignals!
- self assert:((s instVarNamed: #excessSignals) = 0)!
Eliot Miranda uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-eem.796.mcz
==================== Summary ====================
Name: Tools-eem.796
Author: eem
Time: 5 February 2018, 10:55:12.092928 am
UUID: cc39c046-0da0-4dc7-b932-e00c98e1b729
Ancestors: Tools-tpr.795
Show a Character's code in the inspector label, as per SmallInteger.
=============== Diff against Tools-tpr.795 ===============
Item was added:
+ ----- Method: Character>>defaultLabelForInspector (in category '*Tools-Inspecting-label') -----
+ defaultLabelForInspector
+ "Answer the default label to be used for an Inspector window on the receiver."
+
+ ^ super defaultLabelForInspector, ': code ', self asInteger printString!
Item was changed:
----- Method: StandardToolSet class>>inspectorClassOf: (in category 'inspecting') -----
inspectorClassOf: anObject
"Answer the inspector class for the given object. The tool set must know which inspector type to use for which object - the object cannot possibly know what kind of inspectors the toolset provides."
| map |
map := Dictionary new.
#(
+ (CompiledCode CompiledMethodInspector)
- (CompiledMethod CompiledMethodInspector)
(CompositeEvent OrderedCollectionInspector)
(Dictionary DictionaryInspector)
(ExternalStructure ExternalStructureInspector)
(FloatArray OrderedCollectionInspector)
(OrderedCollection OrderedCollectionInspector)
(Set SetInspector)
(WeakSet WeakSetInspector)
) do:[:spec|
map at: spec first put: spec last.
].
anObject class withAllSuperclassesDo:[:cls|
map at: cls name ifPresent:[:inspectorName| ^Smalltalk classNamed: inspectorName].
].
^Inspector!
David T. Lewis uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-tonyg.1150.mcz
==================== Summary ====================
Name: Kernel-tonyg.1150
Author: tonyg
Time: 1 February 2018, 9:59:50.393266 am
UUID: 6afb5455-c040-4572-a62a-b292e20856e6
Ancestors: Kernel-tonyg.1149
(Re)introduce isPromise, and use it instead of isKindOf:.
=============== Diff against Kernel-tonyg.1149 ===============
Item was added:
+ ----- Method: Object>>isPromise (in category 'testing') -----
+ isPromise
+ ^false!
Item was changed:
----- Method: Promise>>isPromise (in category 'testing') -----
isPromise
+ ^ true!
- ^ true.!
Item was changed:
----- Method: Promise>>resolveWith: (in category 'resolving') -----
resolveWith: arg
"Resolve this promise. If arg is itself a Promise, make this promise depend upon it,
as detailed in the Promises/A+ spec:
https://promisesaplus.com/#the-promise-resolution-procedure"
+ arg isPromise
- (arg isKindOf: Promise)
ifTrue: [
arg whenResolved: [:v | self resolveWith: v].
arg whenRejected: [:e | self rejectWith: e]]
ifFalse: [
mutex critical: [
(state == #pending) ifTrue: [
value := arg.
state := #fulfilled.
resolvers do: [:r | self evaluateResolver: r]]]]!
David T. Lewis uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-tonyg.1149.mcz
==================== Summary ====================
Name: Kernel-tonyg.1149
Author: tonyg
Time: 31 January 2018, 11:45:27.771328 pm
UUID: ab03b520-f8a0-4aa9-87c4-9b7e9dfb0369
Ancestors: Kernel-tonyg.1148
Add an alias for Promise>>#then:, called #>>=, echoing the monadic bind operator.
Use of a binary selector allows for convenient chaining of Promises without lots of nested parentheses.
For example:
(self produceSomePromise)
>>= [:v | self produceAnotherPromiseInvolving: v]
>>= [:v | self makeYetAnotherUsing: v]
instead of the more cumbersome
((self produceSomePromise)
then: [:v | self produceAnotherPromiseInvolving: v])
then: [:v | self makeYetAnotherUsing: v]
Also adds #>>, again named after the analogous monadic operator, which is like #>>= but does not pass the value from the left-hand-side promise to the continuation block.
=============== Diff against Kernel-tonyg.1148 ===============
Item was added:
+ ----- Method: Promise>>>> (in category 'monad') -----
+ >> resolvedBlock
+ "Like >>=, but discards the result of the promise."
+ ^ self then: [:v | resolvedBlock value]!
Item was added:
+ ----- Method: Promise>>>>= (in category 'monad') -----
+ >>= resolvedBlock
+ "Alias for `then:` allowing convenient chaining."
+ ^ self then: resolvedBlock!
David T. Lewis uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-tonyg.1148.mcz
==================== Summary ====================
Name: Kernel-tonyg.1148
Author: tonyg
Time: 31 January 2018, 11:37:21.27021 pm
UUID: 2a675301-406b-4222-9599-84358a7cc506
Ancestors: Kernel-eem.1147
Brings the Promise implementation closer to Javascript/A+ promises in two ways:
* Resolving a Promise with another Promise causes them to be chained together
* Resolving or Rejecting a Promise that is not pending is a no-op, like the Firefox/Chrome/etc in-browser promise implementations.
The tests have been changed in KernelTests-tonyg.331 accordingly.
=============== Diff against Kernel-eem.1147 ===============
Item was changed:
Object subclass: #Promise
+ instanceVariableNames: 'value resolvers mutex state error rejecters'
- instanceVariableNames: 'onError value resolvers mutex state error rejectors rejecters'
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Processes'!
+ !Promise commentStamp: 'tonyg 1/31/2018 23:34' prior: 0!
- !Promise commentStamp: 'fbs 5/17/2013 18:23' prior: 0!
I represent the result of an asynchronous message. Once the message is processed, I will be resolved to a value. I am typically instantiated by invocations of #futureSend:at:args: (and not by #futureDo:atArgs:).
See class-comment of FutureNode.
I also implement the Promises/A+ Javascript specification. This allows you to chain my instances to perform arbitrarily complex asynchronous tasks with error handling baked in.
+ A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled (by way of the resolveWith: message), or from #pending -> #rejected (by way of rejectWith:). No other state changes may occur.
+
+ Once #fulfilled or #rejected, a Promise's value must not change. In keeping with the major Javascript Promise implementations' interpretations of this, calls to resolveWith: or rejectWith: when a Promise is in #fulfilled or #rejected state are simply ignored - an error is not signalled. (See test cases PromiseTest testFirstResolutionWins, testCannotRejectFulfilledPromise and testCannotResolveaRejectedPromise.)!
- A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled, or from #pending -> #rejected. No other state changes may occur. Once #fulfilled or #rejected, a Promise's value must change.!
Item was changed:
----- Method: Promise>>rejectWith: (in category 'resolving') -----
rejectWith: anObject
"Reject this promise."
mutex critical: [
+ (state == #pending) ifTrue: [
+ error := anObject.
+ state := #rejected.
+ rejecters do: [:r | self evaluateRejecter: r]]]!
- (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved'].
- (state == #rejected) ifTrue: [self error: 'Promise was already rejected'].
- error := anObject.
- state := #rejected.
- rejecters do: [:r | self evaluateRejecter: r]].!
Item was changed:
----- Method: Promise>>resolveWith: (in category 'resolving') -----
resolveWith: arg
+ "Resolve this promise. If arg is itself a Promise, make this promise depend upon it,
+ as detailed in the Promises/A+ spec:
+ https://promisesaplus.com/#the-promise-resolution-procedure"
+
+ (arg isKindOf: Promise)
+ ifTrue: [
+ arg whenResolved: [:v | self resolveWith: v].
+ arg whenRejected: [:e | self rejectWith: e]]
+ ifFalse: [
+ mutex critical: [
+ (state == #pending) ifTrue: [
+ value := arg.
+ state := #fulfilled.
+ resolvers do: [:r | self evaluateResolver: r]]]]!
- "Resolve this promise"
- mutex critical: [
- (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved'].
- (state == #rejected) ifTrue: [self error: 'Promise was already resolved'].
- value := arg.
- state := #fulfilled.
- resolvers do: [:r |
- self evaluateResolver: r]].!
Item was changed:
----- Method: Promise>>then: (in category 'monad') -----
then: resolvedBlock
+ ^ self then: resolvedBlock ifRejected: [:e | "Pass rejection reason along" e].!
- ^ self then: resolvedBlock ifRejected: [:ignored | "Do nothing"].!
David T. Lewis uploaded a new version of KernelTests to project The Trunk:
http://source.squeak.org/trunk/KernelTests-tonyg.331.mcz
==================== Summary ====================
Name: KernelTests-tonyg.331
Author: tonyg
Time: 31 January 2018, 11:38:54.000311 pm
UUID: 76ecc634-5cf0-43e2-8e17-3dcf9ba18408
Ancestors: KernelTests-nice.330
Updated and new tests for Promise, to match Kernel-tonyg.1148
=============== Diff against KernelTests-nice.330 ===============
Item was changed:
----- Method: PromiseTest>>testCannotRejectFulfilledPromise (in category 'testing - monad') -----
testCannotRejectFulfilledPromise
| p |
p := Promise unit: 1.
+ p rejectWith: Error new.
+ self assert: p isResolved.
+ self assert: 1 equals: p value.
+ !
- self should: [p rejectWith: Error new] raise: Error.!
Item was changed:
----- Method: PromiseTest>>testCannotResolveaRejectedPromise (in category 'testing - monad') -----
testCannotResolveaRejectedPromise
+ | p e |
- | p |
p := Promise new.
+ e := Error new.
+ p rejectWith: e.
+ p resolveWith: 1.
+ self assert: p isRejected.
+ self assert: p error == e.
+ !
- p rejectWith: Error new.
- self should: [p resolveWith: 1] raise: Error.!
Item was added:
+ ----- Method: PromiseTest>>testCollapsesChainsOfPromises (in category 'testing - monad') -----
+ testCollapsesChainsOfPromises
+ "The monadic bind operator has signature (m a -> (a -> m b) -> m b): that is, in our setting,
+ the block given to `then:` is expected to return a *Promise* of a value, not a value directly.
+ It is convenient to accept non-promise values and automatically lift them into the monad,
+ but we must also ensure we treat the case where a `then:`-block yields a Promise correctly."
+ | p q r |
+ p := Promise new.
+ q := p then: [:v | Promise unit: v * 2].
+ r := q then: [:v | Promise unit: v + 1].
+ p resolveWith: 4.
+ self assert: 4 * 2 equals: q value.
+ self assert: (4 * 2 + 1) equals: r value.!
Item was added:
+ ----- Method: PromiseTest>>testFirstResolutionWins (in category 'testing - monad') -----
+ testFirstResolutionWins
+ | p |
+ p := Promise new.
+ p resolveWith: 1.
+ p resolveWith: 2.
+ self assert: p isResolved.
+ self assert: p value == 1.
+ !