Merge pull request #272 from herby/uiless-testrunner

TestSuiteRunner uses fork; impl. fork using pool.
This commit is contained in:
Nicolas Petton 2012-12-28 08:34:08 -08:00
commit fd186dddd9
9 changed files with 548 additions and 12 deletions

View File

@ -3426,6 +3426,88 @@ smalltalk.Set);
smalltalk.addClass('Queue', smalltalk.Object, ['read', 'readIndex', 'write'], 'Kernel-Collections');
smalltalk.addMethod(
"_back_",
smalltalk.method({
selector: "back:",
fn: function (anObject){
var self=this;
smalltalk.send(self["@write"],"_add_",[anObject]);
return self}
}),
smalltalk.Queue);
smalltalk.addMethod(
"_front",
smalltalk.method({
selector: "front",
fn: function (){
var self=this;
var $1;
$1=smalltalk.send(self,"_frontIfAbsent_",[(function(){
return smalltalk.send(self,"_error_",["Cannot read from empty Queue."]);
})]);
return $1;
}
}),
smalltalk.Queue);
smalltalk.addMethod(
"_frontIfAbsent_",
smalltalk.method({
selector: "frontIfAbsent:",
fn: function (aBlock){
var self=this;
var $1,$2,$3;
var $early={};
try {
var result;
result=smalltalk.send(self["@read"],"_at_ifAbsent_",[self["@readIndex"],(function(){
$1=smalltalk.send(self["@write"],"_isEmpty",[]);
if(smalltalk.assert($1)){
$2=smalltalk.send(self["@readIndex"],"__gt",[(1)]);
if(smalltalk.assert($2)){
self["@read"]=[];
self["@read"];
self["@readIndex"]=(1);
self["@readIndex"];
};
$3=smalltalk.send(aBlock,"_value",[]);
throw $early=[$3];
};
self["@read"]=self["@write"];
self["@read"];
self["@readIndex"]=(1);
self["@readIndex"];
self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
self["@write"];
return smalltalk.send(self["@read"],"_first",[]);
})]);
smalltalk.send(self["@read"],"_at_put_",[self["@readIndex"],nil]);
self["@readIndex"]=smalltalk.send(self["@readIndex"],"__plus",[(1)]);
return result;
}
catch(e) {if(e===$early)return e[0]; throw e}
}
}),
smalltalk.Queue);
smalltalk.addMethod(
"_initialize",
smalltalk.method({
selector: "initialize",
fn: function (){
var self=this;
self["@read"]=[];
self["@readIndex"]=(1);
self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
return self}
}),
smalltalk.Queue);
smalltalk.addClass('RegularExpression', smalltalk.Object, [], 'Kernel-Collections');
smalltalk.addMethod(
"_compile_",

View File

@ -4622,6 +4622,108 @@ smalltalk.Set);
smalltalk.addClass('Queue', smalltalk.Object, ['read', 'readIndex', 'write'], 'Kernel-Collections');
smalltalk.addMethod(
"_back_",
smalltalk.method({
selector: "back:",
category: 'accessing',
fn: function (anObject){
var self=this;
smalltalk.send(self["@write"],"_add_",[anObject]);
return self},
args: ["anObject"],
source: "back: anObject\x0a\x09write add: anObject\x0a",
messageSends: ["add:"],
referencedClasses: []
}),
smalltalk.Queue);
smalltalk.addMethod(
"_front",
smalltalk.method({
selector: "front",
category: 'accessing',
fn: function (){
var self=this;
var $1;
$1=smalltalk.send(self,"_frontIfAbsent_",[(function(){
return smalltalk.send(self,"_error_",["Cannot read from empty Queue."]);
})]);
return $1;
},
args: [],
source: "front\x0a ^self frontIfAbsent: [ self error: 'Cannot read from empty Queue.']\x0a",
messageSends: ["frontIfAbsent:", "error:"],
referencedClasses: []
}),
smalltalk.Queue);
smalltalk.addMethod(
"_frontIfAbsent_",
smalltalk.method({
selector: "frontIfAbsent:",
category: 'accessing',
fn: function (aBlock){
var self=this;
var $1,$2,$3;
var $early={};
try {
var result;
result=smalltalk.send(self["@read"],"_at_ifAbsent_",[self["@readIndex"],(function(){
$1=smalltalk.send(self["@write"],"_isEmpty",[]);
if(smalltalk.assert($1)){
$2=smalltalk.send(self["@readIndex"],"__gt",[(1)]);
if(smalltalk.assert($2)){
self["@read"]=[];
self["@read"];
self["@readIndex"]=(1);
self["@readIndex"];
};
$3=smalltalk.send(aBlock,"_value",[]);
throw $early=[$3];
};
self["@read"]=self["@write"];
self["@read"];
self["@readIndex"]=(1);
self["@readIndex"];
self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
self["@write"];
return smalltalk.send(self["@read"],"_first",[]);
})]);
smalltalk.send(self["@read"],"_at_put_",[self["@readIndex"],nil]);
self["@readIndex"]=smalltalk.send(self["@readIndex"],"__plus",[(1)]);
return result;
}
catch(e) {if(e===$early)return e[0]; throw e}
},
args: ["aBlock"],
source: "frontIfAbsent: aBlock\x0a\x09| result |\x0a\x09result := read at: readIndex ifAbsent: [\x0a\x09\x09write isEmpty ifTrue: [\x0a\x09\x09\x09readIndex > 1 ifTrue: [ read := #(). readIndex := 1 ].\x0a\x09\x09\x09^aBlock value ].\x0a \x09read := write.\x0a \x09readIndex := 1.\x0a \x09write := OrderedCollection new.\x0a \x09read first ].\x0a read at: readIndex put: nil.\x0a readIndex := readIndex + 1.\x0a ^result\x0a",
messageSends: ["at:ifAbsent:", "ifTrue:", ">", "value", "isEmpty", "new", "first", "at:put:", "+"],
referencedClasses: ["OrderedCollection"]
}),
smalltalk.Queue);
smalltalk.addMethod(
"_initialize",
smalltalk.method({
selector: "initialize",
category: 'initialization',
fn: function (){
var self=this;
self["@read"]=[];
self["@readIndex"]=(1);
self["@write"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
return self},
args: [],
source: "initialize\x0a\x09read := #().\x0a readIndex := 1.\x0a write := OrderedCollection new",
messageSends: ["new"],
referencedClasses: ["OrderedCollection"]
}),
smalltalk.Queue);
smalltalk.addClass('RegularExpression', smalltalk.Object, [], 'Kernel-Collections');
smalltalk.addMethod(
"_compile_",

View File

@ -36,6 +36,17 @@ return self}
}),
smalltalk.BlockClosure);
smalltalk.addMethod(
"_fork",
smalltalk.method({
selector: "fork",
fn: function (){
var self=this;
smalltalk.send(smalltalk.send((smalltalk.ForkPool || ForkPool),"_default",[]),"_fork_",[self]);
return self}
}),
smalltalk.BlockClosure);
smalltalk.addMethod(
"_new",
smalltalk.method({
@ -450,6 +461,111 @@ smalltalk.CompiledMethod);
smalltalk.addClass('ForkPool', smalltalk.Object, ['poolSize', 'maxPoolSize', 'queue', 'worker'], 'Kernel-Methods');
smalltalk.addMethod(
"_addWorker",
smalltalk.method({
selector: "addWorker",
fn: function (){
var self=this;
smalltalk.send(self["@worker"],"_valueWithTimeout_",[(0)]);
self["@poolSize"]=smalltalk.send(self["@poolSize"],"__plus",[(1)]);
return self}
}),
smalltalk.ForkPool);
smalltalk.addMethod(
"_fork_",
smalltalk.method({
selector: "fork:",
fn: function (aBlock){
var self=this;
var $1;
$1=smalltalk.send(self["@poolSize"],"__lt",[self["@maxPoolSize"]]);
if(smalltalk.assert($1)){
smalltalk.send(self,"_addWorker",[]);
};
smalltalk.send(self["@queue"],"_back_",[aBlock]);
return self}
}),
smalltalk.ForkPool);
smalltalk.addMethod(
"_initialize",
smalltalk.method({
selector: "initialize",
fn: function (){
var self=this;
var $1;
var sentinel;
self["@poolSize"]=(0);
self["@maxPoolSize"]=smalltalk.send(smalltalk.send(self,"_class",[]),"_defaultMaxPoolSize",[]);
self["@queue"]=smalltalk.send((smalltalk.Queue || Queue),"_new",[]);
sentinel=smalltalk.send((smalltalk.Object || Object),"_new",[]);
self["@worker"]=(function(){
var block;
self["@poolSize"]=smalltalk.send(self["@poolSize"],"__minus",[(1)]);
self["@poolSize"];
block=smalltalk.send(self["@queue"],"_frontIfAbsent_",[(function(){
return sentinel;
})]);
block;
$1=smalltalk.send(block,"__eq_eq",[sentinel]);
if(! smalltalk.assert($1)){
return smalltalk.send((function(){
return smalltalk.send(block,"_value",[]);
}),"_ensure_",[(function(){
return smalltalk.send(self,"_addWorker",[]);
})]);
};
});
return self}
}),
smalltalk.ForkPool);
smalltalk.ForkPool.klass.iVarNames = ['default'];
smalltalk.addMethod(
"_default",
smalltalk.method({
selector: "default",
fn: function (){
var self=this;
var $1;
if(($receiver = self["@default"]) == nil || $receiver == undefined){
self["@default"]=smalltalk.send(self,"_new",[]);
$1=self["@default"];
} else {
$1=self["@default"];
};
return $1;
}
}),
smalltalk.ForkPool.klass);
smalltalk.addMethod(
"_defaultMaxPoolSize",
smalltalk.method({
selector: "defaultMaxPoolSize",
fn: function (){
var self=this;
return (100);
}
}),
smalltalk.ForkPool.klass);
smalltalk.addMethod(
"_resetDefault",
smalltalk.method({
selector: "resetDefault",
fn: function (){
var self=this;
self["@default"]=nil;
return self}
}),
smalltalk.ForkPool.klass);
smalltalk.addClass('Message', smalltalk.Object, ['selector', 'arguments'], 'Kernel-Methods');
smalltalk.addMethod(
"_arguments",

View File

@ -52,6 +52,22 @@ referencedClasses: []
}),
smalltalk.BlockClosure);
smalltalk.addMethod(
"_fork",
smalltalk.method({
selector: "fork",
category: 'timeout/interval',
fn: function (){
var self=this;
smalltalk.send(smalltalk.send((smalltalk.ForkPool || ForkPool),"_default",[]),"_fork_",[self]);
return self},
args: [],
source: "fork\x0a\x09ForkPool default fork: self",
messageSends: ["fork:", "default"],
referencedClasses: ["ForkPool"]
}),
smalltalk.BlockClosure);
smalltalk.addMethod(
"_new",
smalltalk.method({
@ -622,6 +638,141 @@ smalltalk.CompiledMethod);
smalltalk.addClass('ForkPool', smalltalk.Object, ['poolSize', 'maxPoolSize', 'queue', 'worker'], 'Kernel-Methods');
smalltalk.addMethod(
"_addWorker",
smalltalk.method({
selector: "addWorker",
category: 'action',
fn: function (){
var self=this;
smalltalk.send(self["@worker"],"_valueWithTimeout_",[(0)]);
self["@poolSize"]=smalltalk.send(self["@poolSize"],"__plus",[(1)]);
return self},
args: [],
source: "addWorker\x0a\x09worker valueWithTimeout: 0.\x0a poolSize := poolSize + 1",
messageSends: ["valueWithTimeout:", "+"],
referencedClasses: []
}),
smalltalk.ForkPool);
smalltalk.addMethod(
"_fork_",
smalltalk.method({
selector: "fork:",
category: 'action',
fn: function (aBlock){
var self=this;
var $1;
$1=smalltalk.send(self["@poolSize"],"__lt",[self["@maxPoolSize"]]);
if(smalltalk.assert($1)){
smalltalk.send(self,"_addWorker",[]);
};
smalltalk.send(self["@queue"],"_back_",[aBlock]);
return self},
args: ["aBlock"],
source: "fork: aBlock\x0a\x09poolSize < maxPoolSize ifTrue: [ self addWorker ].\x0a\x09queue back: aBlock",
messageSends: ["ifTrue:", "addWorker", "<", "back:"],
referencedClasses: []
}),
smalltalk.ForkPool);
smalltalk.addMethod(
"_initialize",
smalltalk.method({
selector: "initialize",
category: 'initialization',
fn: function (){
var self=this;
var $1;
var sentinel;
self["@poolSize"]=(0);
self["@maxPoolSize"]=smalltalk.send(smalltalk.send(self,"_class",[]),"_defaultMaxPoolSize",[]);
self["@queue"]=smalltalk.send((smalltalk.Queue || Queue),"_new",[]);
sentinel=smalltalk.send((smalltalk.Object || Object),"_new",[]);
self["@worker"]=(function(){
var block;
self["@poolSize"]=smalltalk.send(self["@poolSize"],"__minus",[(1)]);
self["@poolSize"];
block=smalltalk.send(self["@queue"],"_frontIfAbsent_",[(function(){
return sentinel;
})]);
block;
$1=smalltalk.send(block,"__eq_eq",[sentinel]);
if(! smalltalk.assert($1)){
return smalltalk.send((function(){
return smalltalk.send(block,"_value",[]);
}),"_ensure_",[(function(){
return smalltalk.send(self,"_addWorker",[]);
})]);
};
});
return self},
args: [],
source: "initialize\x0a\x09| sentinel |\x0a\x09poolSize := 0.\x0a maxPoolSize := self class defaultMaxPoolSize.\x0a queue := Queue new.\x0a sentinel := Object new.\x0a worker := [\x0a\x09\x09| block |\x0a poolSize := poolSize - 1.\x0a\x09\x09block := queue frontIfAbsent: [ sentinel ].\x0a block == sentinel ifFalse: [\x0a \x09[ block value ] ensure: [ self addWorker ]]].",
messageSends: ["defaultMaxPoolSize", "class", "new", "-", "frontIfAbsent:", "ifFalse:", "ensure:", "addWorker", "value", "=="],
referencedClasses: ["Queue", "Object"]
}),
smalltalk.ForkPool);
smalltalk.ForkPool.klass.iVarNames = ['default'];
smalltalk.addMethod(
"_default",
smalltalk.method({
selector: "default",
category: 'accessing',
fn: function (){
var self=this;
var $1;
if(($receiver = self["@default"]) == nil || $receiver == undefined){
self["@default"]=smalltalk.send(self,"_new",[]);
$1=self["@default"];
} else {
$1=self["@default"];
};
return $1;
},
args: [],
source: "default\x0a\x09^default ifNil: [ default := self new ]",
messageSends: ["ifNil:", "new"],
referencedClasses: []
}),
smalltalk.ForkPool.klass);
smalltalk.addMethod(
"_defaultMaxPoolSize",
smalltalk.method({
selector: "defaultMaxPoolSize",
category: 'accessing',
fn: function (){
var self=this;
return (100);
},
args: [],
source: "defaultMaxPoolSize\x0a\x09^100",
messageSends: [],
referencedClasses: []
}),
smalltalk.ForkPool.klass);
smalltalk.addMethod(
"_resetDefault",
smalltalk.method({
selector: "resetDefault",
category: 'accessing',
fn: function (){
var self=this;
self["@default"]=nil;
return self},
args: [],
source: "resetDefault\x0a\x09default := nil",
messageSends: [],
referencedClasses: []
}),
smalltalk.ForkPool.klass);
smalltalk.addClass('Message', smalltalk.Object, ['selector', 'arguments'], 'Kernel-Methods');
smalltalk.Message.comment="Generally, the system does not use instances of Message for efficiency reasons.\x0aHowever, when a message is not understood by its receiver, the interpreter will make up an instance of it in order to capture the information involved in an actual message transmission. \x0aThis instance is sent it as an argument with the message `doesNotUnderstand:` to the receiver.\x0a\x0aSee boot.js, `messageNotUnderstood` and its counterpart `Object>>doesNotUnderstand:`"
smalltalk.addMethod(

View File

@ -533,14 +533,12 @@ return smalltalk.send(self["@result"],"_nextRunDo_",[(function(index){
return smalltalk.send((function(){
return smalltalk.send(self["@result"],"_runCase_",[smalltalk.send(self["@suite"],"_at_",[index])]);
}),"_ensure_",[(function(){
smalltalk.send(worker,"_valueWithTimeout_",[(0)]);
smalltalk.send(worker,"_fork",[]);
return smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
})]);
})]);
});
smalltalk.send(smalltalk.send(smalltalk.send(self["@suite"],"_size",[]),"_min_",[(25)]),"_timesRepeat_",[(function(){
return smalltalk.send(worker,"_valueWithTimeout_",[(0)]);
})]);
smalltalk.send(worker,"_fork",[]);
return self}
}),
smalltalk.TestSuiteRunner);

View File

@ -729,18 +729,16 @@ return smalltalk.send(self["@result"],"_nextRunDo_",[(function(index){
return smalltalk.send((function(){
return smalltalk.send(self["@result"],"_runCase_",[smalltalk.send(self["@suite"],"_at_",[index])]);
}),"_ensure_",[(function(){
smalltalk.send(worker,"_valueWithTimeout_",[(0)]);
smalltalk.send(worker,"_fork",[]);
return smalltalk.send(self["@announcer"],"_announce_",[smalltalk.send(smalltalk.send((smalltalk.ResultAnnouncement || ResultAnnouncement),"_new",[]),"_result_",[self["@result"]])]);
})]);
})]);
});
smalltalk.send(smalltalk.send(smalltalk.send(self["@suite"],"_size",[]),"_min_",[(25)]),"_timesRepeat_",[(function(){
return smalltalk.send(worker,"_valueWithTimeout_",[(0)]);
})]);
smalltalk.send(worker,"_fork",[]);
return self},
args: [],
source: "run\x0a\x09| worker |\x0a\x09result total: suite size.\x0a announcer announce: (ResultAnnouncement new result: result).\x0a worker := [ result nextRunDo: [ :index |\x0a\x09\x09[ result runCase: (suite at: index) ]\x0a\x09\x09ensure: [ worker valueWithTimeout: 0.\x0a \x09announcer announce: (ResultAnnouncement new result: result) ]]].\x0a\x09(suite size min: 25) timesRepeat: [ worker valueWithTimeout: 0 ]",
messageSends: ["total:", "size", "announce:", "result:", "new", "nextRunDo:", "ensure:", "valueWithTimeout:", "runCase:", "at:", "timesRepeat:", "min:"],
source: "run\x0a\x09| worker |\x0a\x09result total: suite size.\x0a announcer announce: (ResultAnnouncement new result: result).\x0a worker := [ result nextRunDo: [ :index |\x0a\x09\x09[ result runCase: (suite at: index) ]\x0a\x09\x09ensure: [ worker fork.\x0a \x09announcer announce: (ResultAnnouncement new result: result) ]]].\x0a\x09worker fork",
messageSends: ["total:", "size", "announce:", "result:", "new", "nextRunDo:", "ensure:", "fork", "runCase:", "at:"],
referencedClasses: ["ResultAnnouncement"]
}),
smalltalk.TestSuiteRunner);

View File

@ -1487,6 +1487,43 @@ includes: anObject
^elements includes: anObject
! !
Object subclass: #Queue
instanceVariableNames: 'read readIndex write'
package: 'Kernel-Collections'!
!Queue methodsFor: 'accessing'!
back: anObject
write add: anObject
!
front
^self frontIfAbsent: [ self error: 'Cannot read from empty Queue.']
!
frontIfAbsent: aBlock
| result |
result := read at: readIndex ifAbsent: [
write isEmpty ifTrue: [
readIndex > 1 ifTrue: [ read := #(). readIndex := 1 ].
^aBlock value ].
read := write.
readIndex := 1.
write := OrderedCollection new.
read first ].
read at: readIndex put: nil.
readIndex := readIndex + 1.
^result
! !
!Queue methodsFor: 'initialization'!
initialize
read := #().
readIndex := 1.
write := OrderedCollection new
! !
Object subclass: #RegularExpression
instanceVariableNames: ''
package: 'Kernel-Collections'!

View File

@ -115,6 +115,10 @@ valueWithPossibleArguments: aCollection
!BlockClosure methodsFor: 'timeout/interval'!
fork
ForkPool default fork: self
!
valueWithInterval: aNumber
<return setInterval(self, aNumber)>
!
@ -208,6 +212,54 @@ source: aString
self basicAt: 'source' put: aString
! !
Object subclass: #ForkPool
instanceVariableNames: 'poolSize maxPoolSize queue worker'
package: 'Kernel-Methods'!
!ForkPool methodsFor: 'action'!
addWorker
worker valueWithTimeout: 0.
poolSize := poolSize + 1
!
fork: aBlock
poolSize < maxPoolSize ifTrue: [ self addWorker ].
queue back: aBlock
! !
!ForkPool methodsFor: 'initialization'!
initialize
| sentinel |
poolSize := 0.
maxPoolSize := self class defaultMaxPoolSize.
queue := Queue new.
sentinel := Object new.
worker := [
| block |
poolSize := poolSize - 1.
block := queue frontIfAbsent: [ sentinel ].
block == sentinel ifFalse: [
[ block value ] ensure: [ self addWorker ]]].
! !
ForkPool class instanceVariableNames: 'default'!
!ForkPool class methodsFor: 'accessing'!
default
^default ifNil: [ default := self new ]
!
defaultMaxPoolSize
^100
!
resetDefault
default := nil
! !
Object subclass: #Message
instanceVariableNames: 'selector arguments'
package: 'Kernel-Methods'!

View File

@ -234,9 +234,9 @@ run
announcer announce: (ResultAnnouncement new result: result).
worker := [ result nextRunDo: [ :index |
[ result runCase: (suite at: index) ]
ensure: [ worker valueWithTimeout: 0.
ensure: [ worker fork.
announcer announce: (ResultAnnouncement new result: result) ]]].
(suite size min: 25) timesRepeat: [ worker valueWithTimeout: 0 ]
worker fork
! !
!TestSuiteRunner methodsFor: 'initialization'!