diff --git a/build.xml b/build.xml
index 7e13533e5..2622753ab 100644
--- a/build.xml
+++ b/build.xml
@@ -406,7 +406,7 @@
-
+
@@ -446,6 +446,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/core-lib/Benchmarks/SavinaSnap.ns b/core-lib/Benchmarks/SavinaSnap.ns
new file mode 100644
index 000000000..f5a5e8c4d
--- /dev/null
+++ b/core-lib/Benchmarks/SavinaSnap.ns
@@ -0,0 +1,2684 @@
+class Savina usingPlatform: platform andHarness: harness = Value (
+| private Benchmark = harness Benchmark.
+ private actors = platform actors.
+ private Array = platform kernel Array.
+ private TransferArray= platform kernel TransferArray.
+ private Vector = platform kernel Vector.
+ private Dictionary= platform collections Dictionary.
+ private system = platform system.
+|
+)(
+ (* A simple PRNG, to be as portable as possible. *)
+ public class Random new: seed = (
+ | private seed ::= seed.
+ private gotNextGaussian ::= false.
+ private nextNextGaussian ::= 0.0. |
+ ) (
+ public next = (
+ seed:: ((seed * 1309) + 13849) & 65535.
+ ^ seed
+ )
+
+ (* Returns an integer within the range of [0, bound) *)
+ public next: bound = (
+ ^ next % bound
+ )
+
+ (* Returns a double uniformly distributed in the range of [0.0, 1.0) *)
+ public nextDouble = (
+ ^ next // 65536
+ )
+
+ public nextBoolean = (
+ ^ next < 32768
+ )
+
+ (* Returns a double normally distributed with mean 0.0
+ and standard deviation of 1.0 *)
+ public nextGaussian = (
+ | v1 v2 s multiplier |
+ gotNextGaussian ifTrue: [
+ gotNextGaussian:: false.
+ ^ nextNextGaussian ].
+
+ v1:: (2.0 * nextDouble) - 1.0.
+ v2:: (2.0 * nextDouble) - 1.0.
+ s:: (v1 * v1) + (v2 * v2).
+
+ [s >= 1.0 or: [s = 0.0]] whileTrue: [
+ v1:: (2.0 * nextDouble) - 1.0.
+ v2:: (2.0 * nextDouble) - 1.0.
+ s:: (v1 * v1) + (v2 * v2).
+ ].
+
+ multiplier:: (-2.0 * s log // s) sqrt.
+ nextNextGaussian:: v2 * multiplier.
+ gotNextGaussian:: true.
+ ^ v1 * multiplier
+ )
+ ) : (
+ public new = (
+ ^ new: 74755
+ )
+ )
+
+ (* === Savina Microbenchmarks === *)
+
+ public class PingPong new: numPings = Benchmark <: Value (
+ | private NumPings = numPings.
+ |
+ )(
+ class Ping new: cnt with: pong = (
+ | private pingsLeft ::= cnt.
+ private pong = pong.
+ |
+ ) (
+ public start = (
+ pong <-: ping: self.
+ pingsLeft:: pingsLeft - 1.
+ )
+
+ public ping = (
+ pong <-: ping: self.
+ (*('ping: ' + pingsLeft) println.*)
+ pingsLeft = (NumPings / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+ pingsLeft:: pingsLeft - 1.
+ )
+
+ public pong: sender = (
+ pingsLeft > 0
+ ifTrue: [ self <-: ping ]
+ ifFalse: [ pong <-: stop ].
+ )
+ )
+
+ class Pong new: completionRes = (
+ | private pongCount ::= 0.
+ private completionRes = completionRes.
+ |
+ ) (
+ public ping: sender = (
+ sender <-: pong: self.
+ pongCount:: pongCount + 1.
+ )
+
+ public stop = (
+ completionRes resolve: pongCount
+ )
+ )
+
+ public benchmark = (
+ | ping pong completionPP |
+ completionPP:: actors createPromisePair.
+ pong:: (actors createActorFromValue: Pong) <-: new: completionPP resolver.
+ ping:: (actors createActorFromValue: Ping) <-: new: NumPings with: pong.
+ ping <-: start.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = NumPings
+ )
+ ) : (
+ public newInstance: problemSize = ( ^ self new: problemSize asInteger )
+ public setupVerifiedRun: run = ( run problemSize: 1 )
+ )
+
+ public class Counting new: limit = Benchmark <: Value (
+ | private limit = limit. |
+ )(
+ public class ProducerActor new: counter resolver: completionRes = (
+ | private counter = counter.
+ private completionRes = completionRes.
+ |
+ )(
+ public increment = (
+ 1 to: limit do: [:i |
+ counter <-: increment ].
+
+ counter <-: requestCount: self.
+ )
+
+ public count: cnt = (
+ counter <-: dummy.
+ completionRes resolve: cnt = limit
+ )
+ )
+
+ public class CountingActor = (
+ | private count ::= 0. |
+ ) (
+ public increment = (
+ count = (limit / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+
+ count:: count + 1.
+ )
+
+ public requestCount: requester = (
+ requester <-: count: count
+ )
+
+ (*we need this because this actor is exceuted only once and doesnt handle its todos *)
+ public dummy = ()
+ )
+
+ public benchmark = (
+ | counter producer completionPP |
+ completionPP:: actors createPromisePair.
+ counter:: (actors createActorFromValue: CountingActor) <-: new.
+ producer:: (actors createActorFromValue: ProducerActor) <-: new: counter resolver: completionPP resolver.
+ producer <-: increment.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: isCorrect = (
+ ^ isCorrect
+ )
+ ) : (
+ public newInstance: problemSize = ( ^ self new: problemSize asInteger )
+ public setupVerifiedRun: run = ( run problemSize: 10000 )
+ )
+
+ public class ForkJoinThroughput new: numActors totalMessages: numMessages = Benchmark <: Value (
+ | private numActors = numActors.
+ private numMessages = numMessages.
+ |
+ )(
+ class ThroughputActor new: completionResolver = (
+ | private messagesProcessed ::= 0.
+ private completionResolver = completionResolver.
+ |
+ )(
+ private performComputation: theta = (
+ | sint res |
+ sint:: theta sin.
+ res:: sint * sint.
+
+ (* defeat dead code elimination *)
+ res <= 0.0 ifTrue: [
+ system error: 'Benchmark calculated unrealistic res value ' + res asString ]
+ )
+
+ public process = (
+ messagesProcessed:: messagesProcessed + 1.
+ self performComputation: 37.2.
+
+ messagesProcessed = numMessages ifTrue: [
+ completionResolver resolve: messagesProcessed
+ ]
+ )
+ )
+
+ public benchmark = (
+ | benchActors promiseGroup |
+ promiseGroup:: nil.
+ benchActors:: Array new: numActors withAll: [
+ | promisePair |
+ promisePair:: actors createPromisePair.
+ promiseGroup
+ ifNil: [ promiseGroup:: promisePair promise ]
+ ifNotNil: [ promiseGroup:: promiseGroup, promisePair promise ].
+ (actors createActorFromValue: ThroughputActor) <-: new: promisePair resolver
+ ].
+
+ 1 to: numMessages do:[ :i|
+ i = (numMessages / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+ benchActors do: [:a | a <-: process ]
+ ].
+
+ ^ promiseGroup
+ )
+
+ public verifyResult: result = (
+ result do: [:n | n = numMessages ifFalse: [ ^ false ] ].
+ ^ true
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self new: (problem at: 1) asInteger
+ totalMessages: (problem at: 2) asInteger
+ )
+ public setupVerifiedRun: run = ( run problemSize: '100:1000' )
+ )
+
+ public class ForkJoinActorCreation new: numActors = Benchmark <: Value (
+ | private numActors = numActors. |
+ )(
+ class ForkJoinActor new: completionResolver = (
+ completionResolver resolve: (performComputation: 37.2)
+ )()
+
+ private performComputation: theta = (
+ | sint res |
+ sint:: theta sin.
+ res:: sint * sint.
+
+ res <= 0.0 ifTrue: [
+ system error: 'Benchmark calculated unrealistic res value ' + res asString ].
+ ^ res
+ )
+
+ public benchmark = (
+ | promiseGroup |
+ promiseGroup:: nil.
+
+ 1 to: numActors do:[ :i|
+ | promisePair |
+ i = (numActors / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+ promisePair:: actors createPromisePair.
+ promiseGroup
+ ifNil: [ promiseGroup:: promisePair promise ]
+ ifNotNil: [ promiseGroup:: promiseGroup, promisePair promise ].
+ (actors createActorFromValue: ForkJoinActor) <-: new: promisePair resolver.
+ ].
+ ^ promiseGroup
+ )
+
+ public verifyResult: resultVector = (
+ | expResult |
+ expResult:: performComputation: 37.2.
+ resultVector do: [:r | r = expResult ifFalse: [ ^ false ] ].
+ ^ true
+ )
+ ) : (
+ public newInstance: problemSize = ( ^ self new: problemSize asInteger )
+ public setupVerifiedRun: run = ( run problemSize: 1 )
+ )
+
+ public class ThreadRing new: numActors numPings: numPings = Benchmark <: Value(
+ | private numActors = numActors.
+ private numPings = numPings.
+ |
+ )(
+ class ThreadRingActor new: id resolver: completionRes = (
+ | private id = id.
+ private nextAct ::= nil.
+ private completionRes = completionRes.
+ |
+ )(
+ public ping: t = (
+ t = (numPings / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+
+ t > 0 ifTrue: [
+ nextAct <-: ping: t - 1
+ ] ifFalse: [
+ nextAct <-: exit: numActors - 1
+ ]
+ )
+
+ public exit: t = (
+ t > 0 ifTrue: [
+ nextAct <-: exit: t - 1
+ ] ifFalse: [
+ completionRes resolve: id.
+ ]
+ )
+
+ public nextActor: actor = (
+ nextAct:: actor
+ )
+ )
+
+ public benchmark = (
+ | threadActors completionPP |
+ completionPP:: actors createPromisePair.
+ threadActors:: Array new: numActors.
+ 1 to: numActors do: [:i |
+ | threadActor |
+ threadActor:: (actors createActorFromValue: ThreadRingActor) <-: new: i resolver: completionPP resolver.
+ threadActors at: i put: threadActor.
+ ].
+
+ 1 to: numActors do: [:i |
+ | nextActor |
+ nextActor:: threadActors at: (i % numActors) + 1.
+ (threadActors at: i) <-: nextActor: nextActor.
+ ].
+
+ (threadActors at: 1) <-: ping: numPings.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = 1
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self new: (problem at: 1) asInteger
+ numPings: (problem at: 2) asInteger
+ )
+ public setupVerifiedRun: run = ( run problemSize: '30:300' )
+ )
+
+ public class Chameneos meetings: numMeetings chameneos: numChameneos = Benchmark <: Value (
+ | private numMeetings = numMeetings.
+ private numChameneos = numChameneos.
+
+ private red = Red new.
+ private yellow = Yellow new.
+ private blue = Blue new.
+ private faded = Faded new.
+ |
+ )(
+ class Red = Value ()(
+ public complement: other = ( ^ other redComplement )
+ public redComplement = ( ^ red )
+ public yellowComplement = ( ^ blue )
+ public blueComplement = ( ^ yellow )
+ )
+ class Yellow = Value ()(
+ public complement: other = ( ^ other yellowComplement )
+ public redComplement = ( ^ blue )
+ public yellowComplement = ( ^ yellow )
+ public blueComplement = ( ^ red )
+ )
+ class Blue = Value ()(
+ public complement: other = ( ^ other blueComplement )
+ public redComplement = ( ^ yellow )
+ public yellowComplement = ( ^ red )
+ public blueComplement = ( ^ blue )
+ )
+ class Faded = Value ()(
+ public complement: other = ( ^ faded )
+ )
+
+ class ChameneosMallActor new: completionRes = (
+ | private waitingChameneo ::= nil.
+ private sumMeetings ::= 0.
+ private numFaded ::= 0.
+ private n ::= numMeetings.
+ private completionRes = completionRes.
+ |
+ start.
+ )(
+ private color: anInt = (
+ | colorIdx |
+ colorIdx:: anInt % 3.
+ colorIdx = 0 ifTrue: [ ^ red ].
+ colorIdx = 1 ifTrue: [ ^ yellow ].
+ colorIdx = 2 ifTrue: [ ^ blue ].
+ )
+
+ private start = (
+ 0 to: numChameneos - 1 do: [:i |
+ | color |
+ color:: color: i.
+ (actors createActorFromValue: ChameneosChameneoActor) <-: new: self color: color
+ ]
+ )
+
+ public meetingCount: count = (
+ numFaded:: numFaded + 1.
+ sumMeetings:: sumMeetings + count.
+ numFaded = numChameneos ifTrue: [ completionRes resolve: sumMeetings ]
+ )
+
+ public meet: sender color: color = (
+ n > 0 ifTrue: [
+ waitingChameneo
+ ifNil: [ waitingChameneo:: sender ]
+ ifNotNil: [
+ n = (numMeetings / 2) ifTrue:[
+ actors snapshot: self.
+ ].
+ n:: n - 1.
+ waitingChameneo <-: meet: sender color: color.
+ waitingChameneo:: nil.
+ ]
+ ] ifFalse: [
+ sender <-: exit: self
+ ]
+ )
+ )
+
+ class ChameneosChameneoActor new: mall color: color = (
+ | private mall = mall.
+ private color ::= color.
+ private meetings ::= 0.
+ |
+ start.
+ )(
+ private start = (
+ mall <-: meet: self color: color.
+ )
+
+ public meet: sender color: otherColor = (
+ | complement |
+ complement:: color complement: otherColor.
+ meetings:: meetings + 1.
+ sender <-: change: color.
+ mall <-: meet: self color: color.
+ )
+
+ public change: newColor = (
+ color:: newColor.
+ meetings:: meetings + 1.
+ mall <-: meet: self color: newColor.
+ )
+
+ public exit: sender = (
+ color:: faded.
+ sender <-: meetingCount: meetings
+ )
+ )
+
+ public benchmark = (
+ | mallActor completionPP |
+ completionPP:: actors createPromisePair.
+ mallActor:: (actors createActorFromValue: ChameneosMallActor) <-: new: completionPP resolver.
+ ^ completionPP promise
+ )
+
+ public verifyResult: sumMeetings = (
+ ^ sumMeetings = (2 * numMeetings)
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self meetings: (problem at: 1) asInteger
+ chameneos: (problem at: 2) asInteger
+ )
+ public setupVerifiedRun: run = ( run problemSize: '100:200000' )
+ )
+
+ (* Does not send the unnecessary exit messages because of using the promise
+ for completion. *)
+ public class BigContention numWorkers: numWorkers numMessages: numMessages = Benchmark <: Value (
+ | private numMessages = numMessages.
+ private numWorkers = numWorkers.
+ |
+ )(
+ class BigActor new: id sink: sinkActor = (
+ | private numPings ::= 0.
+ private expPinger ::= -1.
+ public neighbors ::= nil.
+ private random = Random new: id.
+ private id = id.
+ private sinkActor = sinkActor.
+ |)(
+ public ping: sender = (
+ (neighbors at: sender) <-: pong: id.
+ )
+
+ public pong: sender = (
+ sender = expPinger ifFalse: [
+ error: 'ERROR: expected: ' + expPinger asString + ' but received from ' + sender asString ].
+
+ numPings = numMessages
+ ifTrue: [ sinkActor <-: exit ]
+ ifFalse: [
+ sendPing.
+ numPings:: numPings + 1 ]
+ )
+
+ private sendPing = (
+ | target |
+ target:: (random next: neighbors size) + 1.
+ expPinger:: target.
+ (neighbors at: target) <-: ping: id
+ )
+ )
+
+ class SinkActor new: completionRes = (
+ | private numMessages ::= 0.
+ public neighbors ::= nil.
+ private completionRes = completionRes.
+ |)(
+ public exit = (
+ numMessages:: numMessages + 1.
+ numMessages = numWorkers ifTrue: [ completionRes resolve: numMessages ]
+ )
+ )
+
+ public benchmark = (
+ | sinkActor bigActors unwrapPromise completionPP |
+ completionPP:: actors createPromisePair.
+ sinkActor:: (actors createActorFromValue: SinkActor) <-: new: completionPP resolver.
+
+ bigActors:: TransferArray new: numWorkers.
+ bigActors doIndexes: [:i |
+ bigActors at: i put: ((actors createActorFromValue: BigActor) <-: new: i sink: sinkActor)].
+
+ (* we unwrap the promises here to avoid sending chained promises to all
+ the actors. If the promise is not yet resolved (a race condition)
+ they will end up chained, which is also expensive, especially since
+ there need to be send n * m messages instead 2n for the original one
+ and the callback *)
+ unwrapPromise:: actors async: 1 to: numWorkers do: [:i |
+ (bigActors at: i) whenResolved: [:farRef | bigActors at: i put: farRef] ].
+
+ unwrapPromise whenResolved: [:r |
+ sinkActor <-: neighbors: bigActors.
+ bigActors do: [:n | n <-: neighbors: bigActors ].
+ bigActors do: [:n | n <-: pong: -1 ].
+ actors snapshot: self.
+ ].
+
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = numWorkers
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numWorkers: (problem at: 1) asInteger
+ numMessages: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '20000:120'
+ )
+ )
+
+ (* === Savina Concurrency Benchmarks === *)
+
+ public class ConcurrentDictionary numEntities: numEntities numMessages: numMessages writePercentage: writePercentage = Benchmark <: Value (
+ | private numMessages = numMessages.
+ private numEntities = numEntities.
+ private writePercentage = writePercentage.
+ private dataLimit = 512. (* 524287 / 1024, Integer.MAX_VALUE / 4_096 TODO: this is not the same constant as originally! *)
+ |
+ )(
+ class Master new: completionRes = (
+ | private workers = Array new: numEntities.
+ private dictionary = (actors createActorFromValue: DictionaryActor) <-: new: completionRes.
+ private numWorkersTerminated ::= 0.
+ |
+ start.
+ )(
+ private start = (
+ workers doIndexes: [:i |
+ | worker |
+ worker:: (actors createActorFromValue: Worker) <-: new: self dict: dictionary id: i.
+ workers at: i put: worker.
+ worker <-: doWork.
+ ].
+
+ actors snapshot: self.
+ )
+
+ public endWork = (
+ numWorkersTerminated:: numWorkersTerminated + 1.
+ numWorkersTerminated = numEntities ifTrue: [
+ dictionary <-: endWork.
+ ]
+ )
+ )
+
+ class Worker new: master dict: dictionary id: id = (
+ | private messageCount ::= 0.
+ private random = Random new: id + numMessages + writePercentage.
+ private master = master.
+ private dictionary = dictionary.
+ |
+ )(
+ public doWork = (
+ messageCount:: messageCount + 1.
+ messageCount <= numMessages
+ ifTrue: [
+ | rnd |
+ rnd:: random next % 100.
+ rnd < writePercentage
+ ifTrue: [
+ dictionary <-: write: self key: random next % dataLimit value: random next ]
+ ifFalse: [
+ dictionary <-: read: self key: random next % dataLimit ] ]
+ ifFalse: [
+ master <-: endWork
+ ].
+ )
+
+ public result: value = (
+ self doWork
+ )
+ )
+
+ class DictionaryActor new: completionRes = (
+ | private dataMap = self createDataMap: dataLimit.
+ private completionRes = completionRes.
+ |
+ )(
+ private createDataMap: dataLimit = (
+ | dict |
+ dict:: Dictionary new: dataLimit.
+ 0 to: dataLimit - 1 do: [:i |
+ dict at: i put: i
+ ].
+ ^ dict
+ )
+
+ public write: sender key: key value: val = (
+ dataMap at: key put: val.
+ sender <-: result: val
+ )
+
+ public read: sender key: key = (
+ sender <-: result: (dataMap at: key).
+ )
+
+ public endWork = (
+ completionRes resolve: dataMap size
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = dataLimit
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numEntities: (problem at: 1) asInteger
+ numMessages: (problem at: 2) asInteger
+ writePercentage: (problem at: 3) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '100:100:50'
+ )
+ )
+
+ public class ConcurrentSortedLinkedList numWorkers: w numMessagesPerWorker: mw writePercentage: wp sizePercentage: sp = Benchmark <: Value (
+ | private numWorkers = w.
+ private numMessagesPerWorker = mw.
+ private writePercentage = wp.
+ private sizePercentage = sp.
+ |)(
+ class Master new: completionRes = (
+ | private workers = Array new: numWorkers.
+ private sortedList = (actors createActorFromValue: SortedList) <-: new: completionRes.
+ private numWorkersTerminated ::= 0.
+ |
+ workers doIndexes: [:i |
+ workers at: i put: ((actors createActorFromValue: Worker) <-: new: self sortedList: sortedList id: i).
+ (workers at: i) <-: doWork.
+ ].
+
+ actors snapshot: self.
+ )(
+ public endWork = (
+ numWorkersTerminated:: numWorkersTerminated + 1.
+ numWorkersTerminated = numWorkers ifTrue: [
+ sortedList <-: endWork
+ ]
+ )
+ )
+
+ class Worker new: master sortedList: sortedList id: id = (
+ | private master = master.
+ private sortedList = sortedList.
+ private messageCount ::= 0.
+ private random = Random new: id + numMessagesPerWorker + writePercentage + sizePercentage.
+ |)(
+ public endWork = (
+ messageCount:: messageCount + 1.
+ master <-: endWork.
+ )
+
+ public doWork = (
+ messageCount:: messageCount + 1.
+ messageCount <= numMessagesPerWorker
+ ifTrue: [
+ | anInt |
+ anInt:: random next % 100.
+ anInt < sizePercentage
+ ifTrue: [ sortedList <-: size: self.
+ ^ self ].
+ anInt < (sizePercentage + writePercentage)
+ ifTrue: [ sortedList <-: write: random next sender: self.
+ ^ self].
+ sortedList <-: contains: random next sender: self ]
+ ifFalse: [
+ master <-: endWork
+ ]
+ )
+
+ public result: val = (
+ self doWork
+ )
+ )
+
+ class SortedList new: completionRes = (
+ | private completionRes = completionRes.
+ private dataList = SortedLinkedList new.
+ |)(
+ public write: anInt sender: sender = (
+ dataList add: anInt.
+ sender <-: result: anInt
+ )
+
+ public contains: anInt sender: sender = (
+ | result |
+ result:: dataList contains: anInt.
+ sender <-: result: result.
+ )
+
+ public size: sender = (
+ sender <-: result: dataList size
+ )
+
+ public endWork = (
+ completionRes resolve: dataList size
+ )
+ )
+
+ class SortedLinkedList = (
+ | private head ::= nil.
+ private iterator ::= nil.
+ |)(
+ class Node new: i = (
+ | public item ::= i.
+ public next ::= nil.
+ |)()
+
+ public isEmpty = ( ^ head isNil )
+
+ public add: item = (
+ | newNode after before |
+ newNode:: Node new: item.
+ head ifNil: [
+ head:: newNode.
+ ^ self ].
+
+ item < head item ifTrue: [
+ newNode next: head.
+ head:: newNode.
+ ^ self ].
+
+ after:: head next.
+ before:: head.
+
+ [ after notNil and: [ item >= after item] ] whileTrue: [
+ before:: after.
+ after:: after next.
+ ].
+
+ newNode next: before next.
+ before next: newNode.
+ )
+
+ public contains: item = (
+ | n |
+ n:: head.
+ [ n notNil ] whileTrue: [
+ item = n item ifTrue: [ ^ true ].
+ n:: n next.
+ ].
+ ^ false
+ )
+
+ public size = (
+ | r n |
+ r:: 0.
+ n:: head.
+ [ n notNil ] whileTrue: [
+ r:: r + 1.
+ n:: n next
+ ].
+ ^ r
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ (numWorkers = 10 and: [numMessagesPerWorker = 20 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 18 ].
+ (numWorkers = 10 and: [numMessagesPerWorker = 100 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 106 ].
+ (numWorkers = 10 and: [numMessagesPerWorker = 300 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 292 ].
+ (numWorkers = 10 and: [numMessagesPerWorker = 500 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 476 ].
+ (numWorkers = 10 and: [numMessagesPerWorker = 1000 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 992 ].
+ (numWorkers = 10 and: [numMessagesPerWorker = 1500 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 1496 ].
+ (numWorkers = 20 and: [numMessagesPerWorker = 1000 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 1959 ].
+ (numWorkers = 20 and: [numMessagesPerWorker = 2000 and: [writePercentage = 10 and: [sizePercentage = 1]]]) ifTrue: [ ^ result = 3956 ].
+
+ (* otherwise, warn that we don't know whether it is correct. *)
+ ('---- result: ' + result asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numWorkers: (problem at: 1) asInteger
+ numMessagesPerWorker: (problem at: 2) asInteger
+ writePercentage: (problem at: 3) asInteger
+ sizePercentage: (problem at: 4) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '20:8000:10:1'
+ )
+ )
+
+ public class ProducerConsumerBoundedBuffer bufferSize: bs numProducers: np numConsumers: nc numItemsPerProducer: ni = Benchmark <: Value (
+ | private bufferSize = bs.
+ private numProducers = np.
+ private numConsumers = nc.
+ private numItemsPerProducer = ni.
+ private prodCost = 25.
+ private consCost = 25.
+ |)(
+ class Data new: datum from: sender = Value (
+ | public datum = datum.
+ public sender = sender.|)()
+
+ class ManagerActor new: completionRes = (
+ | private adjustedBufferSize = bufferSize - numProducers.
+ private availableProducers = Vector new.
+ private availableConsumers = Vector new.
+ private pendingData = Vector new.
+ private numTerminatedProducers ::= 0.
+ private producers = Array new: numProducers.
+ private consumers = Array new: numConsumers.
+ private completionRes = completionRes.
+ private dataSum ::= 0.0.
+ |
+ producers doIndexes: [:i |
+ | producer |
+ producer:: (actors createActorFromValue: ProducerActor) <-: new: i manager: self.
+ producers at: i put: producer.
+ producer <-: produceData.
+ ].
+ consumers doIndexes: [:i |
+ | consumer |
+ consumer:: (actors createActorFromValue: ConsumerActor) <-: new: i manager: self.
+ consumers at: i put: consumer.
+ availableConsumers append: consumer.
+ ].
+
+ actors snapshot: self.
+ )(
+ public data: datum from: producer = (
+ dataSum:: dataSum + datum.
+ availableConsumers isEmpty
+ ifTrue: [ pendingData append: (Data new: datum from: producer) ]
+ ifFalse: [ availableConsumers removeFirst <-: data: datum from: producer ].
+
+ pendingData size >= adjustedBufferSize
+ ifTrue: [ availableProducers append: producer ]
+ ifFalse: [ producer <-: produceData ]
+ )
+
+ public consume: consumer = (
+ pendingData isEmpty
+ ifTrue: [
+ availableConsumers append: consumer.
+ tryExit ]
+ ifFalse: [
+ | data |
+ data:: pendingData removeFirst.
+ consumer <-: data: data datum from: data sender.
+ availableProducers isEmpty ifFalse: [
+ availableProducers removeFirst <-: produceData ] ].
+ )
+
+ public producerExit = (
+ numTerminatedProducers:: numTerminatedProducers + 1.
+ tryExit
+ )
+
+ private tryExit = (
+ (numTerminatedProducers = numProducers and: [availableConsumers size = numConsumers])
+ ifTrue: [
+ consumers do: [:c | c <-: exit ].
+ completionRes resolve: dataSum ].
+ )
+ )
+
+ class ProducerActor new: id manager: manager = (
+ | private prodItem ::= 0.0.
+ private itemsProduced ::= 0.
+ private manager = manager.
+ |)(
+ private prodData = (
+ prodItem:: processItem: prodItem cost: prodCost.
+ manager <-: data: prodItem from: self.
+ itemsProduced:: itemsProduced + 1.
+ )
+
+ public produceData = (
+ itemsProduced < numItemsPerProducer
+ ifTrue: [ self prodData ]
+ ifFalse: [ manager <-: producerExit ]
+ )
+ )
+
+ class ConsumerActor new: id manager: manager = (
+ | private consItem ::= 0.0.
+ private manager = manager.
+ |)(
+ private consumeDataItem: dataToConsume = (
+ consItem:: processItem: consItem + dataToConsume cost: consCost.
+ )
+
+ public data: datum from: sender = (
+ consumeDataItem: datum.
+ manager <-: consume: self
+ )
+
+ public exit = ()
+ )
+
+ private processItem: curTerm cost: cost = (
+ | res random |
+ res:: curTerm.
+ random:: Random new: cost.
+
+ cost > 0
+ ifTrue: [
+ cost timesRepeat: [
+ 100 timesRepeat: [
+ res:: res + (random nextDouble abs + 0.01) log ] ] ]
+ ifFalse: [
+ res:: res + (random nextDouble abs + 0.01) log ].
+ ^ res
+ )
+
+ public benchmark = (
+ | manager completionPP |
+ completionPP:: actors createPromisePair.
+ manager:: (actors createActorFromValue: ManagerActor) <-: new: completionPP resolver.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ (bufferSize = 40 and: [numProducers = 5 and: [numConsumers = 5 and: [numItemsPerProducer = 10]]]) ifTrue: [^ result round = -654026].
+ (bufferSize = 40 and: [numProducers = 10 and: [numConsumers = 10 and: [numItemsPerProducer = 60]]]) ifTrue: [^ result round = -43522486].
+ (bufferSize = 40 and: [numProducers = 10 and: [numConsumers = 10 and: [numItemsPerProducer = 80]]]) ifTrue: [^ result round = -77056204].
+ (bufferSize = 40 and: [numProducers = 20 and: [numConsumers = 20 and: [numItemsPerProducer = 80]]]) ifTrue: [^ result round = -154112409].
+ (bufferSize = 50 and: [numProducers = 20 and: [numConsumers = 20 and: [numItemsPerProducer = 100]]]) ifTrue: [^ result round = -240206069].
+ (bufferSize = 50 and: [numProducers = 40 and: [numConsumers = 40 and: [numItemsPerProducer = 100]]]) ifTrue: [^ result round = -480412139].
+ (bufferSize = 40 and: [numProducers = 10 and: [numConsumers = 10 and: [numItemsPerProducer = 600]]]) ifTrue: [^ result round = -4288035081].
+
+ (* otherwise, warn that we don't know whether it is correct. *)
+ ('---- result: ' + result asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self bufferSize: (problem at: 1) asInteger
+ numProducers: (problem at: 2) asInteger
+ numConsumers: (problem at: 3) asInteger
+ numItemsPerProducer: (problem at: 4) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '50:40:40:1000'
+ )
+ )
+
+ public class Philosophers new: numPhil rounds: numRounds = Benchmark <: Value (
+ | private numPhil = numPhil.
+ private numRounds = numRounds.
+ |)(
+ private class Counter = (
+ | private value ::= 0. |
+ )(
+ public inc: anInt = ( value:: value + anInt. )
+ public get = ( ^ value )
+ )
+
+ private class PhilosopherActor new: id rounds: rounds counter: aCounter arbitrator: arbitrator = (
+ | private localCounter ::= 0.
+ private roundsSoFar ::= 0.
+ private id = id.
+ private rounds = rounds.
+ private arbitrator = arbitrator.
+ private counter = aCounter.
+ |)(
+ public denied = (
+ localCounter:: localCounter + 1.
+ arbitrator <-: hungry: self id: id.
+ )
+
+ public eat = (
+ roundsSoFar:: roundsSoFar + 1.
+ localCounter > 0 ifTrue: [
+ counter <-: inc: localCounter.
+ localCounter:: 0. ].
+
+ arbitrator <-: done: id.
+ roundsSoFar < rounds
+ ifTrue: [ self <-: start ]
+ ifFalse: [ arbitrator <-: exit ]
+ )
+
+ public start = (
+ arbitrator <-: hungry: self id: id.
+ )
+ )
+
+ private class ArbitratorActor new: numForks resolver: resolver = (
+ | private numForks = numForks.
+ private forks = Array new: numForks withAll: false.
+ private numExitedPhilosophers ::= 0.
+ private resolver = resolver.
+ |)(
+ public hungry: philosopher id: leftForkId = (
+ | rightForkId |
+ rightForkId:: 1 + ((leftForkId + 1) % numForks).
+
+ ((forks at: leftForkId) or: [forks at: rightForkId])
+ ifTrue: [ philosopher <-: denied ]
+ ifFalse: [
+ forks at: leftForkId put: true.
+ forks at: rightForkId put: true.
+ philosopher <-: eat ]
+ )
+
+ public done: leftForkId = (
+ | rightForkId |
+ rightForkId:: 1 + ((leftForkId + 1) % numForks).
+
+ forks at: leftForkId put: false.
+ forks at: rightForkId put: false.
+ )
+
+ public exit = (
+ numExitedPhilosophers:: numExitedPhilosophers + 1.
+
+ numForks = numExitedPhilosophers ifTrue: [
+ | forksTaken |
+ forksTaken:: 0.
+ forks do: [:f | f ifTrue: [ forksTaken:: forksTaken + 1 ] ].
+
+ resolver resolve: forksTaken ]
+ )
+ )
+
+ public benchmark = (
+ | counter completionPP arbitrator philosophers |
+ counter:: Counter new.
+ completionPP:: actors createPromisePair.
+
+ arbitrator:: (actors createActorFromValue: ArbitratorActor) <-: new: numPhil resolver: completionPP resolver.
+ philosophers:: Array new: numPhil.
+ philosophers doIndexes: [:i |
+ | ph |
+ ph:: (actors createActorFromValue: PhilosopherActor) <-: new: i
+ rounds: numRounds counter: counter arbitrator: arbitrator.
+ philosophers at: i put: ph ].
+
+ philosophers do: [:ph | ph <-: start ].
+ actors snapshot: self.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = 0
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self new: (problem at: 1) asInteger
+ rounds: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '20:10000'
+ )
+ )
+
+ public class SleepingBarber numHaircuts: numHaircuts waitingRoomSize: waitingRoomSize avProductionRate: avProductionRate avHaircutRate: avHaircutRate = Benchmark <: Value (
+ | private numHaircuts = numHaircuts.
+ private waitingRoomSize = waitingRoomSize.
+ private avProductionRate = avProductionRate.
+ private avHaircutRate = avHaircutRate.
+ |)(
+ private class WaitingRoomActor new: capacity barber: anActor = (
+ | private waitingCustomers = Vector new.
+ private barberAsleep ::= true.
+ private barber = anActor.
+ private capacity = capacity.
+ |)(
+ public enter: customer in: room = (
+ waitingCustomers size = capacity
+ ifTrue: [ customer <-: full ]
+ ifFalse: [
+ waitingCustomers append: customer.
+ barberAsleep
+ ifTrue: [
+ barberAsleep:: false.
+ self <-: next ]
+ ifFalse: [
+ customer <-: wait ] ]
+ )
+
+ public next = (
+ waitingCustomers size > 0
+ ifTrue: [
+ | customer |
+ customer:: waitingCustomers removeFirst.
+ barber <-: enter: customer in: self ]
+ ifFalse: [
+ barber <-: wait.
+ barberAsleep:: true ]
+ )
+
+ public exit = ( barber <-: exit )
+ )
+
+ private class BarberActor new: resolver = (
+ | private random = Random new.
+ private resolver = resolver.
+ |)(
+ public enter: customer in: room = (
+ customer <-: start.
+ busyWait: (random next: avHaircutRate) + 10.
+ customer <-: done.
+ room <-: next
+ )
+
+ private busyWait: limit = (
+ | test |
+ test:: 0.
+ limit timesRepeat: [
+ random next.
+ test:: test + 1 ].
+ ^ test
+ )
+
+ public wait = ()
+ public exit = ( resolver resolve: random next )
+ )
+
+ private class CustomerFactoryActor new: numHaircuts room: room = (
+ | private random = Random new.
+ private numHaircuts = numHaircuts.
+ private room = room.
+ private numHaircutsSoFar ::= 0.
+ private idGenerator ::= 0.
+ |)(
+ public start = (
+ 1 to: numHaircuts do:[ :i |
+ i = (numHaircuts / 2) ifTrue: [
+ actors snapshot: self.
+ ].
+ sendCustomerToRoom.
+ busyWait: (random next: avHaircutRate) + 10 ].
+ )
+
+ private busyWait: limit = (
+ | test |
+ test:: 0.
+ limit timesRepeat: [
+ random next.
+ test:: test + 1 ].
+ ^ test
+ )
+
+ public returned: customer = (
+ idGenerator:: idGenerator + 1.
+ sendCustomerToRoom: customer
+ )
+
+ public done = (
+ numHaircutsSoFar:: numHaircutsSoFar + 1.
+ numHaircutsSoFar = numHaircuts ifTrue: [
+ room <-: exit ]
+ )
+
+ private sendCustomerToRoom = (
+ | customer |
+ customer:: (actors createActorFromValue: CustomerActor) <-: new: idGenerator factory: self.
+ idGenerator:: idGenerator + 1.
+ sendCustomerToRoom: customer.
+ )
+
+ private sendCustomerToRoom: customer = (
+ room <-: enter: customer in: room
+ )
+ )
+
+ private class CustomerActor new: id factory: factoryActor = (
+ | private factoryActor = factoryActor.
+ |)(
+ public full = ( factoryActor <-: returned: self )
+ public wait = ()
+ public start= ()
+ public done = ( factoryActor <-: done )
+ )
+
+ public benchmark = (
+ | barber room factoryActor completionPP |
+ completionPP:: actors createPromisePair.
+
+ barber:: (actors createActorFromValue: BarberActor) <-: new: completionPP resolver.
+ room:: (actors createActorFromValue: WaitingRoomActor) <-: new: waitingRoomSize barber: barber.
+ factoryActor:: (actors createActorFromValue: CustomerFactoryActor) <-: new: numHaircuts room: room.
+
+ factoryActor <-: start.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ (numHaircuts = 800 and: [avHaircutRate = 200]) ifTrue: [ ^ result = 11357 ].
+ (numHaircuts = 1000 and: [avHaircutRate = 500]) ifTrue: [ ^ result = 5029 ].
+ (numHaircuts = 2500 and: [avHaircutRate = 500]) ifTrue: [ ^ result = 16033 ].
+ (numHaircuts = 2500 and: [avHaircutRate = 1000]) ifTrue: [ ^ result = 32109 ].
+ (numHaircuts = 5000 and: [avHaircutRate = 1000]) ifTrue: [ ^ result = 32109 ].
+
+ ('no verification result for avHaircutRate: ' + avHaircutRate + ' result is: ' + result) println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self numHaircuts: (problem at: 1) asInteger
+ waitingRoomSize: (problem at: 2) asInteger
+ avProductionRate: (problem at: 3) asInteger
+ avHaircutRate: (problem at: 4) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '5000:1000:1000:1000'
+ )
+ )
+
+ public class CigaretteSmokers rounds: rounds smokers: smokers = Benchmark <: Value (
+ | private rounds = rounds.
+ private smokers = smokers.
+ |)(
+ private class ArbiterActor new: numRounds smokers: numSmokers resolver: resolver = (
+ | private numRounds = numRounds.
+ private numSmokers = numSmokers.
+ private resolver = resolver.
+
+ private smokers = Array new: numSmokers withAll: [ (actors createActorFromValue: SmokerActor) <-: new: self ].
+ private random = Random new.
+ private roundsSoFar ::= 0.
+ private smokersExited ::= numSmokers.
+ |)(
+ public start = ( notifyRandomSmoker )
+
+ public startedSmoking = (
+ roundsSoFar:: roundsSoFar + 1.
+ roundsSoFar = (numRounds / 2) ifTrue: [
+ actors snapshot: self.
+ ].
+
+ roundsSoFar >= numRounds
+ ifTrue: [ requestSmokersToExit ]
+ ifFalse: [ notifyRandomSmoker ]
+ )
+
+ private notifyRandomSmoker = (
+ | newSmokerIndex busyWaitPeriod |
+ newSmokerIndex:: 1 + (random next abs) % numSmokers.
+ busyWaitPeriod:: 10 + (random next: 1000).
+ (smokers at: newSmokerIndex) <-: startSmoking: busyWaitPeriod
+ )
+
+ private requestSmokersToExit = (
+ smokers do: [:s | s <-: exit ]
+ )
+
+ public exited = (
+ smokersExited:: smokersExited - 1.
+ smokersExited = 0 ifTrue: [
+ resolver resolve: random next ]
+ )
+ )
+
+ private class SmokerActor new: arbiterActor = (
+ | private arbiterActor = arbiterActor.
+ private random = Random new.
+ |)(
+ public startSmoking: busyWaitPeriod = (
+ arbiterActor <-: startedSmoking.
+ busyWait: busyWaitPeriod
+ )
+
+ private busyWait: limit = (
+ | test |
+ test:: 0.
+ limit timesRepeat: [
+ random next.
+ test:: test + 1 ].
+ ^ test
+ )
+
+ public exit = (
+ arbiterActor <-: exited
+ )
+ )
+
+ public benchmark = (
+ | arbiterActor completionPP |
+ completionPP:: actors createPromisePair.
+
+ arbiterActor:: (actors createActorFromValue: ArbiterActor) <-: new: rounds smokers: smokers resolver: completionPP resolver.
+ arbiterActor <-: start.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ (rounds = 1000 and: [ smokers = 200 ]) ifTrue: [ ^ result = 50272 ].
+ (rounds = 2000 and: [ smokers = 200 ]) ifTrue: [ ^ result = 11088 ].
+ (rounds = 10000 and: [ smokers = 200 ]) ifTrue: [ ^ result = 53968 ].
+ ('no verification result for rounds: ' + rounds + ' smokers: ' + smokers + ' result is: ' + result) println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self rounds: (problem at: 1) asInteger
+ smokers: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '1000:200'
+ )
+ )
+
+ public class LogisticsMapSeries numTerms: terms numSeries: series startRate: rate = Benchmark <: Value (
+ | private numTerms = terms.
+ private numSeries = series.
+ private startRate = rate.
+ private increment = 0.0025.
+ |)(
+ class Master new: completionRes = (
+ | private computers = Array new: numSeries.
+ private workers = Array new: numSeries.
+
+ private numWorkRequested ::= 0.
+ private numWorkReceived ::= 0.
+ private termsSum ::= 0.0.
+
+ private completionRes = completionRes.
+ |
+ computers doIndexes: [:i |
+ | rate i0 |
+ i0:: i - 1.
+ rate:: startRate + (i0 * increment).
+ computers at: i put: ((actors createActorFromValue: RateComputer) <-: new: rate) ].
+ workers doIndexes: [:i |
+ | rateComputer startTerm i0 |
+ i0:: i - 1.
+ rateComputer:: computers at: i.
+ startTerm:: i0 * increment.
+ workers at: i put: ((actors createActorFromValue: SeriesWorker) <-: new: i master: self rateComputer: rateComputer startTerm: startTerm) ].
+ )(
+ public start = (
+ 1 to: numTerms do: [:i |
+ workers do: [:w | w <-: nextTerm ] ].
+
+ workers do: [:w |
+ w <-: getTerm.
+ numWorkRequested:: numWorkRequested + 1 ]
+ )
+
+ public result: term = (
+ termsSum:: termsSum + term.
+ numWorkReceived:: numWorkReceived + 1.
+ numWorkReceived = (numWorkRequested / 2) ifTrue: [
+ actors snapshot: self.
+ ].
+
+ numWorkRequested = numWorkReceived ifTrue: [
+ (* don't need to tell them to stop. will be GCed automatically. *)
+ completionRes resolve: termsSum ]
+ )
+ )
+
+ class SeriesWorker new: id master: master rateComputer: computer startTerm: startTerm = (
+ | private curTerm ::= startTerm.
+ private master = master.
+ private computer = computer.
+ private waitingForReply ::= false.
+ private waitingNextTerm ::= 0.
+ private waitingGetTerm ::= false.
+ |)(
+ public nextTerm = (
+ waitingForReply
+ ifTrue: [ waitingNextTerm:: waitingNextTerm + 1 ]
+ ifFalse: [
+ computer <-: compute: self term: curTerm.
+ waitingForReply:: true ]
+ )
+
+ public result: term = (
+ waitingNextTerm > 0
+ ifTrue: [
+ waitingNextTerm:: waitingNextTerm - 1.
+ computer <-: compute: self term: term ]
+ ifFalse: [
+ curTerm:: term.
+ waitingForReply:: false.
+ waitingGetTerm ifTrue: [
+ master <-: result: term ] ]
+ )
+
+ public getTerm = (
+ waitingForReply
+ ifTrue: [ waitingGetTerm:: true ]
+ ifFalse: [ master <-: result: curTerm ]
+ )
+ )
+
+ class RateComputer new: rate = (
+ | private rate = rate. |
+ )(
+ public compute: sender term: term = (
+ | res |
+ res:: rate * term * (1 - term).
+ sender <-: result: res
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ master <-: start.
+ ^ completionPP promise
+ )
+ public verifyResult: result = (
+ | r |
+ r:: (result * 1000000) round.
+ (numSeries = 10 and: [startRate = 3.46]) ifTrue: [ ^ r = 6387835 ].
+ (numSeries = 20 and: [startRate = 3.46]) ifTrue: [ ^ r = 11022424 ].
+
+ ('---- result: ' + r asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self numTerms: (problem at: 1) asInteger
+ numSeries: (problem at: 2) asInteger
+ startRate: (problem at: 3) asInteger // 100
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '25000:10:346'
+ )
+ )
+
+ public class BankTransaction numAccounts: acc numTransactions: tran = Benchmark <: Value (
+ | private numAccounts = acc.
+ private numTransactions = tran.
+ |)(
+ class Teller new: completionRes = (
+ | private accounts = Array new: numAccounts.
+ private numCompletedBankings ::= 0.
+ private randomGen = Random new.
+ private completionRes = completionRes.
+ private transferredAmount ::= 0.0.
+ |
+ accounts doIndexes: [:i |
+ accounts at: i put: ((actors createActorFromValue: Account) <-: new: i)]
+ )(
+ public start = (
+ 1 to: numTransactions do: [:i |
+ i = (numTransactions / 2) ifTrue: [
+ actors snapshot: self.
+ ].
+ generateWork
+ ]
+ )
+
+ public reply: amount = (
+ transferredAmount:: transferredAmount + amount.
+ numCompletedBankings:: numCompletedBankings + 1.
+ numCompletedBankings = numTransactions ifTrue: [
+ completionRes resolve: transferredAmount.
+ ]
+ )
+
+ private generateWork = (
+ | srcAccountId loopId destAccountId srcAccount destAccount amount |
+ srcAccountId:: randomGen next: numAccounts / 10 * 8.
+ loopId:: randomGen next: numAccounts - srcAccountId.
+ loopId = 0 ifTrue: [ loopId:: loopId + 1 ].
+ destAccountId:: srcAccountId + loopId.
+
+ srcAccount:: accounts at: srcAccountId + 1.
+ destAccount:: accounts at: destAccountId + 1.
+ amount:: (randomGen nextDouble abs) * 1000.
+
+ srcAccount <-: credit: amount to: destAccount reply: self
+ )
+ )
+
+ class Account new: id = (
+ | private balance ::= 0.0.
+ private lastSender ::= nil.
+ private waitingForReply ::= false.
+ private requests = Vector new.
+ private id ::= id.
+ |
+ )(
+ class Request credit: amount to: destAccount reply: sender = (
+ | public amount = amount.
+ public destAccount = destAccount.
+ public sender = sender.
+ |)()
+
+ public debit: amount from: sender = (
+ balance:: balance + amount.
+ sender <-: reply: amount.
+ )
+
+ private processCredit: amount to: destAccount reply: sender = (
+ balance:: balance - amount.
+ destAccount <-: debit: amount from: self.
+
+ lastSender:: sender.
+ )
+
+ public credit: amount to: destAccount reply: sender = (
+ waitingForReply
+ ifTrue: [ requests append: (
+ Request credit: amount to: destAccount reply: sender) ]
+ ifFalse: [
+ waitingForReply:: true.
+ processCredit: amount to: destAccount reply: sender
+ ]
+ )
+
+ public reply: amount = (
+ lastSender <-: reply: amount.
+ requests isEmpty
+ ifTrue: [ waitingForReply:: false ]
+ ifFalse: [
+ | req |
+ req:: requests removeFirst.
+ processCredit: req amount to: req destAccount reply: req sender
+ ].
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Teller) <-: new: completionPP resolver.
+ master <-: start.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ | r |
+ r:: result round.
+
+ numTransactions = 100 ifTrue: [ ^ r = 52558 ].
+ numTransactions = 1000 ifTrue: [ ^ r = 516215 ].
+ numTransactions = 10000 ifTrue: [ ^ r = 4975454 ].
+ numTransactions = 50000 ifTrue: [ ^ r = 25057792 ].
+ numTransactions = 100000 ifTrue: [ ^ r = 50038643 ].
+ numTransactions = 500000 ifTrue: [ ^ r = 250052558 ].
+
+ ('---- result: ' + r asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+
+ ^ self numAccounts: (problem at: 1) asInteger
+ numTransactions: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '1000:50000'
+ )
+ )
+
+ (* === Savina Parallelism Benchmarks === *)
+
+ public class RadixSort numValues: numValues maxValue: maxValue seed: seed = Benchmark <: Value (
+ | private numValues = numValues.
+ private maxValue = maxValue. (* Needs to be a power of 2, I think *)
+ private seed = seed. (* Should probably be a prime number *)
+ |
+ )(
+ class IntSourceActor = (
+ | private random = Random new: seed. |
+ ) (
+ public next: actor = (
+ 1 to: numValues do: [:i |
+ | candidate |
+ candidate:: (random next % maxValue) abs.
+ actor <-: value: candidate.
+ ]
+ )
+ )
+
+ class SortActor new: radix next: nextActor = (
+ | private radix = radix.
+ private next = nextActor.
+
+ private orderingArray = Array new: numValues withAll: 0.
+ private valuesSoFar ::= 0.
+ private j ::= 1.
+ |
+ )(
+ public value: current = (
+ valuesSoFar:: valuesSoFar + 1.
+
+ (current & radix) = 0
+ ifTrue: [
+ next <-: value: current ]
+ ifFalse: [
+ orderingArray at: j put: current.
+ j:: j + 1
+ ].
+
+ valuesSoFar = numValues ifTrue: [
+ 1 to: j - 1 do: [:i |
+ next <-: value: (orderingArray at: i)
+ ].
+ ]
+ )
+ )
+
+ class ValidationActor new: completionRes = (
+ | private sumSoFar ::= 0.
+ private valuesSoFar ::= 0.
+ private prevValue ::= 0.
+ private errorValue ::= -1.
+ private errorIdx ::= -1.
+ private completionRes = completionRes.
+ |
+ )(
+ public value: val = (
+ valuesSoFar:: valuesSoFar + 1.
+
+ (val < prevValue and: [errorValue < 0]) ifTrue: [
+ errorValue:: val.
+ errorIdx:: valuesSoFar.
+ system error: 'ERROR: Value out of place: ' + errorValue + ' at index ' + errorIdx
+ ].
+
+ prevValue:: val.
+ sumSoFar:: sumSoFar + prevValue.
+
+ valuesSoFar = numValues ifTrue: [
+ errorValue >= 0
+ ifTrue: [ system error: 'Value out of place: ' + errorValue + ' at index ' + errorIdx ].
+ completionRes resolve: sumSoFar
+ ]
+ )
+ )
+
+ public benchmark = (
+ | validationActor sourceActor radix nextActor completionPP |
+ completionPP:: actors createPromisePair.
+ validationActor:: (actors createActorFromValue: ValidationActor) <-: new: completionPP resolver.
+ sourceActor:: (actors createActorFromValue: IntSourceActor) <-: new.
+
+ radix:: maxValue / 2.
+ nextActor:: validationActor.
+
+ [radix > 0] whileTrue: [
+ | sortActor |
+ sortActor:: (actors createActorFromValue: SortActor) <-: new: radix next: nextActor.
+
+ radix:: radix / 2.
+ nextActor:: sortActor
+ ].
+
+ sourceActor <-: next: nextActor.
+
+ actors snapshot: self.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ (numValues = 100 and: [maxValue = 256 and: [seed = 74755]]) ifTrue: [ ^ result = 13606 ].
+ (numValues = 10000 and: [maxValue = 65536 and: [seed = 74755]]) ifTrue: [ ^ result = 329373752 ].
+ (numValues = 50000 and: [maxValue = 65536 and: [seed = 74755]]) ifTrue: [ ^ result = 1642300184 ].
+
+ ('---- result: ' + result asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numValues: (problem at: 1) asInteger
+ maxValue: (problem at: 2) asInteger
+ seed: (problem at: 3) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '100:256:74755'
+ )
+ )
+
+ public class FilterBank = Benchmark <: Value ()( todo = () )
+
+ public class Sieve new: limit local: numMaxLocalPrimes = Benchmark <: Value (
+ | private limit = limit.
+ private numMaxLocalPrimes = numMaxLocalPrimes.
+ |
+ )(
+ class NumberProducerActor = ()(
+ public produceNumbersFor: filterActor = (
+ | candidate |
+ candidate:: 3.
+ [candidate < limit] whileTrue: [
+ filterActor <-: filter: candidate.
+ candidate:: candidate + 2
+ ].
+
+ filterActor <-: exit.
+ )
+ )
+
+ class PrimeFilterActor new: id initialPrime: initialPrime resolver: completionRes = (
+ | private id = id.
+ private initialPrime = initialPrime.
+ private nextFilterActor ::= nil.
+ private localPrimes = Array new: numMaxLocalPrimes withAll: 0.
+ private availableLocalPrimes ::= 1.
+ private completionRes = completionRes.
+ |
+ localPrimes at: 1 put: initialPrime.
+ )(
+ private handleNewPrim: newPrim = (
+ availableLocalPrimes < numMaxLocalPrimes
+ ifTrue: [
+ (* store locally if there is space *)
+ availableLocalPrimes:: availableLocalPrimes + 1.
+ localPrimes at: availableLocalPrimes put: newPrim ]
+ ifFalse: [
+ (* create a new actor to store a new prime *)
+ nextFilterActor:: (actors createActorFromValue: PrimeFilterActor)
+ <-: new: id + 1 initialPrime: newPrim resolver: completionRes
+ ]
+ )
+
+ private isLocallyPrime: candidate = (
+ 1 to: availableLocalPrimes do: [:i |
+ | remainder |
+ remainder:: candidate % (localPrimes at: i).
+ remainder = 0 ifTrue: [ ^ false ]
+ ].
+ ^ true
+ )
+
+ public filter: candidate = (
+ (isLocallyPrime: candidate)
+ ifTrue: [
+ nextFilterActor
+ ifNil: [handleNewPrim: candidate]
+ ifNotNil: [nextFilterActor <-: filter: candidate]
+ ]
+ )
+ public exit = (
+ nextFilterActor
+ ifNil: [
+ | totalPrimes |
+ totalPrimes:: ((id - 1) * numMaxLocalPrimes) + availableLocalPrimes.
+ completionPP resolve: totalPrimes ]
+ ifNotNil: [ nextFilterActor <-: exit ]
+ )
+ )
+
+ public benchmark = (
+ | producerActor filterActor completionPP |
+ completionPP:: actors createPromisePair.
+ producerActor:: (actors createActorFromValue: NumberProducerActor) <-: new.
+ filterActor:: (actors createActorFromValue: PrimeFilterActor) <-: new: 1 initialPrime: 2 resolver: completionPP resolver.
+
+ producerActor <-: produceNumbersFor: filterActor.
+
+ ^ completionPP promise
+ )
+
+ public verifyResult: numberOfPrimes = (
+ limit = 100 ifTrue: [ ^ numberOfPrimes = 25 ].
+ limit = 1000 ifTrue: [ ^ numberOfPrimes = 168 ].
+ limit = 10000 ifTrue: [ ^ numberOfPrimes = 1229 ].
+ limit = 100000 ifTrue: [ ^ numberOfPrimes = 9592 ].
+ (* otherwise, we don't know. *)
+ ^ true
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self new: (problem at: 1) asInteger
+ local: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '100000:64'
+ )
+ )
+
+ (* Compared to the Savina original benchmarks, this does not contain code for
+ 'urgent' nodes, which seemed broken in the original, at least I did not
+ see that it was used. This version also uses explicit messages to
+ have deterministic termination after the traversals is complete.
+ It does not do an extra traversal for termination, since we already
+ got the upward traversal after #traverse *)
+ public class UnbalancedCobwebbedTree nodes: maxNodes binomial: numChildren avgCompSize: avg stdevCompSize: stdev = Benchmark <: Value (
+ | private numChildren = numChildren.
+ private avgCompSize = avg.
+ private stdevCompSize = stdev.
+ private maxNodes = maxNodes.
+ |)(
+ class RootActor new: completionRes = (
+ | private ran = Random new.
+ private height ::= 1.
+ private size ::= 1.
+ private children = Array new: numChildren.
+ private hasGrantChildren = Array new: numChildren withAll: false.
+ private traversedChildren ::= 0.
+ private subtreeSize ::= 0.
+ private startedTraversal ::= false.
+ private completionRes = completionRes.
+ |)(
+ public generateTree = (
+ | computationSize i j |
+ height:: height + 1.
+ computationSize:: getNextNormal: avgCompSize with: stdevCompSize.
+
+ 1 to: numChildren do: [:i |
+ | nodeActor |
+ nodeActor:: (actors createActorFromValue: NodeActor)
+ <-: parent: self root: self height: height id: size + 1 computation: computationSize.
+ children at: i put: nodeActor.
+ ].
+
+ size:: size + numChildren.
+
+ children do: [:c | c <-: tryGeneratedChildren ].
+ actors snapshot: self.
+ )
+
+ public shouldGenerateChildren: child height: childHeight = (
+ size + numChildren <= maxNodes
+ ifTrue: [
+ | moreChildren |
+ moreChildren:: ran nextBoolean.
+ moreChildren ifTrue: [
+ | childComp randomInt |
+ childComp:: getNextNormal: avgCompSize with: stdevCompSize.
+ child <-: generateChildren: size computation: childComp.
+ size:: size + numChildren.
+
+ childHeight + 1 > height ifTrue: [ height:: childHeight + 1 ].
+ ] ifFalse: [
+ childHeight > height ifTrue: [ height:: childHeight ] ] ]
+ ifFalse: [
+ startedTraversal ifFalse: [
+ startedTraversal:: true.
+ traverse ] ]
+ )
+
+ public traversed: treeSize = (
+ traversedChildren:: traversedChildren + 1.
+ subtreeSize:: subtreeSize + treeSize.
+
+ traversedChildren = numChildren ifTrue: [
+ completionRes resolve: subtreeSize.
+ ]
+ )
+
+ public updateGrant: childId = (
+ hasGrantChildren at: childId put: true
+ )
+
+ private getNextNormal: pMean with: pDev = (
+ | result |
+ result:: 0.
+ [ result <= 0 ] whileTrue: [
+ | tempDouble |
+ tempDouble:: ran nextGaussian * pDev + pMean.
+ result:: tempDouble round
+ ].
+ ^ result
+ )
+
+ private traverse = (
+ children do: [:c | c <-: traverse ].
+ )
+ )
+
+ class NodeActor parent: parent root: root height: height id: id computation: compSize = (
+ | private hasChildren ::= false.
+ private traversedChildren ::= 0.
+ private subtreeSize ::= 0.
+ private children = Array new: numChildren.
+ private hasGrantChildren = Array new: numChildren withAll: false.
+ private busyLoopRan = Random new.
+
+ private myParent = parent.
+ private myRoot = root.
+ private myHeight = height.
+ private myId = id.
+ private myCompSize = compSize.
+ |
+ )(
+ public tryGeneratedChildren = (
+ loop: avgCompSize / 50 with: busyLoopRan.
+ myRoot <-: shouldGenerateChildren: self height: myHeight.
+ )
+
+ public generateChildren: currentId computation: compSize = (
+ | myArrayId childrenHeight idValue cnt |
+ myArrayId:: myId % numChildren.
+ myParent <-: updateGrant: myArrayId.
+ childrenHeight:: myHeight + 1.
+ idValue:: currentId.
+
+ 1 to: numChildren do: [:i |
+ | node |
+ node:: (actors createActorFromValue: NodeActor) <-: parent: self root: myRoot height: childrenHeight id: idValue + 1 computation: compSize.
+ children at: i put: node.
+ ].
+
+ hasChildren:: true.
+ cnt:: 0.
+
+ children do: [:c | c <-: tryGeneratedChildren ].
+ )
+
+ public updateGrant: childId = (
+ hasGrantChildren at: childId put: true
+ )
+
+ public traverse = (
+ traversedChildren:: 0.
+ loop: myCompSize with: busyLoopRan.
+ hasChildren
+ ifTrue: [ children do: [:c | c <-: traverse ] ]
+ ifFalse: [ myParent <-: traversed: 1 ].
+ )
+
+ public traversed: treeSize = (
+ subtreeSize:: subtreeSize + treeSize.
+ traversedChildren:: traversedChildren + 1.
+ traversedChildren = numChildren ifTrue: [
+ myParent <-: traversed: subtreeSize + 1.
+ ]
+ )
+
+ private loop: times with: ran = (
+ | result |
+ result:: 0.
+ 1 to: times do: [:k |
+ result:: ran next
+ ].
+ ^ result
+ )
+ )
+
+ public benchmark = (
+ | rootActor completionPP |
+ completionPP:: actors createPromisePair.
+ rootActor:: (actors createActorFromValue: RootActor) <-: new: completionPP resolver.
+ rootActor <-: generateTree.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ ^ result = (maxNodes - numChildren)
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self nodes: (problem at: 1) asInteger
+ binomial: (problem at: 2) asInteger
+ avgCompSize: (problem at: 3) asInteger
+ stdevCompSize: (problem at: 4) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '200000:10:500:100'
+ )
+ )
+
+ public class TrapezoidalApproximation numWorkers: w numPieces: n left: l right: r = Benchmark <: Value (
+ | private numWorkers = w.
+ private numPieces = n.
+ private left = l.
+ private right = r.
+ private precision = (r - l) // n.
+ |)(
+ class Master new: completionRes = (
+ | private workers = Array new: numWorkers withAll: ((actors createActorFromValue: Worker) <-: new: self).
+ private completionRes = completionRes.
+ private numTermsReceived ::= 0.
+ private resultArea ::= 0.0.
+ |)(
+ public result: result = (
+ numTermsReceived:: numTermsReceived + 1.
+ resultArea:: resultArea + result.
+
+ numTermsReceived = (numWorkers / 2) ifTrue: [
+ actors snapshot: self.
+ ].
+
+ numTermsReceived = numWorkers ifTrue: [
+ completionRes resolve: resultArea ]
+ )
+
+ public work: l and: r and: h = (
+ | workerRange |
+ workerRange:: (r - l) // numWorkers.
+ workers doIndexes: [:i |
+ | wl wr |
+ wl:: (workerRange * (i - 1.0)) + l.
+ wr:: wl + workerRange.
+ (workers at: i) <-: work: wl and: wr and: h
+ ]
+ )
+ )
+
+ class Worker new: master = (
+ | private master = master.
+ |)(
+ public work: l and: r and: h = (
+ | n accumArea |
+ n:: (r - l) // h.
+ accumArea:: 0.0.
+
+ 0 to: n - 1 do: [:i |
+ | lx rx ly ry area |
+ lx:: (i * h) + l.
+ rx:: lx + h.
+
+ ly:: fx: lx.
+ ry:: fx: rx.
+
+ area:: 0.5 * (ly + ry) * h.
+ accumArea:: accumArea + area.
+ ].
+
+ master <-: result: accumArea
+ )
+
+ private fx: x = (
+ | a b c d r |
+ a:: ((x * x * x) - 1.0) sin.
+ b:: x + 1.0.
+ c:: a // b.
+ d:: (1.0 + (2.0 * x) sqrt exp) sqrt.
+ r:: c * d.
+ ^ r
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ master <-: work: left and: right and: precision.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ | r |
+ r:: (result * 100000000) round.
+ (numWorkers = 100 and: [numPieces = 2500 and: [left = 1 and: [right = 5]]]) ifTrue: [ ^ r = 27107880 ].
+ (numWorkers = 100 and: [numPieces = 5000 and: [left = 1 and: [right = 5]]]) ifTrue: [ ^ r = 27108026 ].
+ (numWorkers = 100 and: [numPieces = 10000 and: [left = 1 and: [right = 5]]]) ifTrue: [ ^ r = 27108063 ].
+ (numWorkers = 100 and: [numPieces = 100000 and: [left = 1 and: [right = 5]]]) ifTrue: [ ^ r = 27108075 ].
+ (numWorkers = 100 and: [numPieces = 1000000 and: [left = 1 and: [right = 5]]]) ifTrue: [ ^ r = 27108075 ].
+
+ (* otherwise, warn that we don't know whether it is correct. *)
+ ('---- result: ' + r asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numWorkers: (problem at: 1) asInteger
+ numPieces: (problem at: 2) asInteger
+ left: (problem at: 3) asInteger * 1.0
+ right: (problem at: 4) asInteger * 1.0
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '100:10000000:1:5'
+ )
+ )
+
+
+ public class SuccessiveOverRelaxation = Benchmark <: Value ()( todo = ( USES_SHARED_ARRAYS ) )
+
+ (* This benchmark uses originally shared grid nodes.
+ The interesting property is that the updates are conceptually safe even
+ when they are racy. The benchmark just tries to establish the parent
+ relationship. However, we actually need the information about whether we
+ succeeded with the update to determine which actor should continue
+ processing the updated node.
+
+ For this version, we will use Grid actors that actually hold the grid nodes
+ to avoid sequentializing the access during the search phase.
+
+ Note further, that the #search:target: method is much more complex since
+ it is needs to be fully asynchronous.
+ The #initializeData method is also more complex. I unrolled the loop to
+ avoid the complexity of having to handle the asynchronicity there as well.
+
+ Another critical difference is that we do not calculate the distance from
+ root, because it is not used. And pretty annoying to implement properly
+ synchronized. Which it isn't in the original code either.
+ Also, FarRefs are not polymorphic with local refs, which makes this very
+ strange.
+ *)
+ public class AStarSearch numWorkers: numWorkers gridSize: gridSize = Benchmark <: Value (
+ | private numWorkers = numWorkers.
+ private gridSize = gridSize.
+ private threshold = 1024. |
+ )(
+ class GridNode id: id i: i j: j k: k = (
+ | public id = id.
+ public i = i.
+ public j = j.
+ public k = k.
+ private neighbors = Vector new.
+ public parentInPath ::= nil.
+ public distanceFromRoot ::= 0.
+ |
+ clearParentAndDistance.
+ )(
+ public clearParentAndDistance = (
+ parentInPath:: nil.
+ distanceFromRoot:: id = 1 ifTrue: [0] ifFalse: [-1].
+ )
+
+ private addNeighbor: node = (
+ node == self ifTrue: [ ^ false ].
+ neighbors do: [:n | n == node ifTrue: [ ^ false ] ].
+
+ neighbors append: node.
+ ^ true
+ )
+
+ public addNeighborsA: a b: b c: c d: d e: e f: f addA: addA addB: addB addC: addC addD: addD addE: addE addF: addF = (
+ | addedNeighbor |
+ addedNeighbor:: false.
+
+ addA ifTrue: [ (addNeighbor: a) ifTrue: [ addedNeighbor:: true ] ].
+ addB ifTrue: [ (addNeighbor: b) ifTrue: [ addedNeighbor:: true ] ].
+ addC ifTrue: [ (addNeighbor: c) ifTrue: [ addedNeighbor:: true ] ].
+ addD ifTrue: [ (addNeighbor: d) ifTrue: [ addedNeighbor:: true ] ].
+ addE ifTrue: [ (addNeighbor: e) ifTrue: [ addedNeighbor:: true ] ].
+ (addedNeighbor not or: [addF])
+ ifTrue: [ addNeighbor: f ].
+ )
+
+ public numNeighbors = (
+ ^ neighbors size
+ )
+
+ public neighbor: id = (
+ ^ neighbors at: id
+ )
+
+ public setParent: node = (
+ parentInPath == nil ifFalse: [ ^ false ].
+
+ parentInPath:: node.
+
+ (* node could be a far reference here, not sure how to get this right:
+ distanceFromRoot:: node distanceFromRoot + (distanceFrom: node). *)
+ ^ true
+ )
+
+ private distanceFrom: node = (
+ (* Not implemented!
+ | iDiff jDiff kDiff |
+ iDiff:: i - node i.
+ jDiff:: j - node j.
+ kDiff:: k - node k.
+ ^ ((iDiff * iDiff) + (jDiff * jDiff) + (kDiff * kDiff)) sqrt round *)
+ ^ self
+ )
+ )
+
+ class Master new: completionRes = (
+ | private workers = Array new: numWorkers withAll: [(actors createActorFromValue: Worker) <-: new: self].
+ private numWorkersTerminated ::= 0.
+ private numWorkSent ::= 0.
+ private numWorkCompleted ::= 0.
+ private allNodes = Array new: gridSize * gridSize * gridSize.
+ private completionRes = completionRes.
+ |
+ initializeData whenResolved: [:r | sendWork: originNode target: targetNode ]
+ )(
+ private originNode = (
+ ^ allNodes at: 1
+ )
+
+ private targetNode = (
+ | axisVal targetId |
+ axisVal:: (0.8 * gridSize) round.
+ targetId:: (axisVal * gridSize * gridSize) + (axisVal * gridSize) + axisVal + 1.
+ ^ allNodes at: targetId
+ )
+
+ private sendWork: origin target: target = (
+ | workerIdx |
+ workerIdx:: numWorkSent % numWorkers + 1.
+ numWorkSent:: numWorkSent + 1.
+ (workers at: workerIdx) <-: work: origin target: target.
+ )
+
+ private createGridNodes: gridActors = (
+ | id gs1 |
+ gs1:: gridSize - 1. (* for offset-based numbers *)
+ id:: 1.
+ ^ actors async: 0 to: gs1 do: [:i |
+ | gridActor |
+ gridActor:: gridActors at: (i / 3) + 1.
+
+ actors async: 0 to: gs1 do: [:j |
+ actors async: 0 to: gs1 do: [:k |
+ (gridActor <-: id: id i: i j: j k: k) whenResolved: [:gridNode |
+ allNodes at: id put: gridNode.
+ id:: id + 1 ] ] ] ].
+ )
+
+ private setNeighbors = (
+ | random id gs1 g2 |
+ gs1:: gridSize - 1. (* for offset-based numbers *)
+ g2:: gridSize * gridSize.
+
+ random:: Random new.
+ id:: 1.
+
+ (* We unrolled this loop to avoid having to rewrite it with #whenResolved callbacks.
+ This is certainly not nice code, and not idiomatic either...
+
+ | iterCount neighborCount |
+ iterCount:: 0.
+ neighborCount:: 0.
+
+ 0 to: 1 do: [:i |
+ 0 to: 1 do: [:j |
+ 0 to: 1 do: [:k |
+ iterCount:: iterCount + 1.
+ iterCount = 1 or: [iterCount = 8] ifFalse: [
+ | addNeighbor |
+ addNeighbor:: (iterCount = 7 and: [neighborCount = 0]) or: [random nextBoolean].
+ addNeighbor ifTrue: [
+ | newI newJ newK newId newNode |
+ newI:: (gridSize - 1) min: (gridNode i + i).
+ newJ:: (gridSize - 1) min: (gridNode j + j).
+ newK:: (gridSize - 1) min: (gridNode k + k).
+ newId:: (gridSize * gridSize * newI) + (gridSize * newJ) + newK + 1.
+ newNode:: allNodes at: newId.
+
+ (gridNode addNeighbor: newNode)
+ ifTrue: [ neighborCount:: neighborCount + 1 ] ] ] ] ] ] ]. *)
+
+ 0 to: gs1 do: [:i |
+ 0 to: gs1 do: [:j |
+ 0 to: gs1 do: [:k |
+ (allNodes at: id) <-: addNeighborsA: (allNodes at: (g2 * (gs1 min: i ))
+ + (gridSize * (gs1 min: j ))
+ + (gs1 min: k + 1) + 1)
+ b: (allNodes at: (g2 * (gs1 min: i ))
+ + (gridSize * (gs1 min: j + 1))
+ + (gs1 min: k ) + 1)
+ c: (allNodes at: (g2 * (gs1 min: i ))
+ + (gridSize * (gs1 min: j + 1))
+ + (gs1 min: k + 1) + 1)
+ d: (allNodes at: (g2 * (gs1 min: i + 1))
+ + (gridSize * (gs1 min: j ))
+ + (gs1 min: k ) + 1)
+ e: (allNodes at: (g2 * (gs1 min: i + 1))
+ + (gridSize * (gs1 min: j ))
+ + (gs1 min: k + 1) + 1)
+ f: (allNodes at: (g2 * (gs1 min: i + 1))
+ + (gridSize * (gs1 min: j + 1))
+ + (gs1 min: k ) + 1)
+ addA: random nextBoolean
+ addB: random nextBoolean
+ addC: random nextBoolean
+ addD: random nextBoolean
+ addE: random nextBoolean
+ addF: random nextBoolean.
+ id:: id + 1 ] ] ]
+ )
+
+ private initializeData = (
+ | gridActors |
+ gridActors:: Array new: (gridSize / 3) + 1 withAll: [actors createActorFromValue: GridNode].
+
+ ^ (createGridNodes: gridActors) whenResolved: [:r |
+ setNeighbors.
+ allNodes do: [:gridNode |
+ (* This does not need synchronization, because we rely on the fact
+ that message order is guaranteed from the same sending actor,
+ and that the addNeighbors* message doesn't do any message sending
+ either. *)
+ gridNode <-: clearParentAndDistance ] ]
+ )
+
+ public work: origin target: target = (
+ sendWork: origin target: target
+ )
+
+ public received = (
+ numWorkCompleted:: numWorkCompleted + 1.
+ numWorkCompleted = numWorkSent ifTrue: [
+ requestWorkersToStop ]
+ )
+
+ public done = (
+ requestWorkersToStop
+ )
+
+ public stop = (
+ numWorkersTerminated:: numWorkersTerminated + 1.
+ numWorkersTerminated = numWorkers ifTrue: [
+ completionRes resolve: validate
+ ]
+ )
+
+ private validate = (
+ | parentNode nextProm next loop |
+ loop:: actors createPromisePair.
+ parentNode:: targetNode.
+
+ nextProm:: parentNode <-: parentInPath.
+ nextProm whenResolved: [:next |
+ | n |
+ n:: next.
+ loop resolve: (actors async: [(n == nil) not] whileTrue: [
+ parentNode:: n.
+ (parentNode <-: parentInPath) whenResolved: [:nn | n:: nn] ]) ].
+
+ ^ loop promise whenResolved: [:r | parentNode == originNode]
+ )
+
+ private requestWorkersToStop = (
+ workers do: [:w | w <-: stop ]
+ )
+ )
+
+ class Worker new: master = (
+ | private master = master.
+ private random = Random new. |
+ )(
+ private busyLoop = (
+ 1 to: 100 do: [:i | random next ]
+ )
+
+ public work: origin target: target = (
+ (search: origin target: target)
+ whenResolved: [:r | master <-: received ]
+ )
+
+ public stop = (
+ master <-: stop
+ )
+
+ private search: origin target: target = (
+ | workQueue nodesProcessed continue outerLoopProm |
+ workQueue:: Vector new.
+ workQueue append: origin.
+ continue:: true.
+
+ nodesProcessed:: 0.
+
+ outerLoopProm:: actors async: [
+ workQueue isEmpty not and: [nodesProcessed < threshold and: continue]] whileTrue: [
+ | loopNode numNeighbors outerIterationDone |
+ outerIterationDone:: actors createPromisePair.
+ nodesProcessed:: nodesProcessed + 1.
+ busyLoop.
+
+ loopNode:: workQueue removeFirst.
+ (loopNode <-: numNeighbors) whenResolved: [:numNeighbors |
+ | i whileCompletionPromise |
+ i:: 1.
+
+ whileCompletionPromise:: actors async: [i <= numNeighbors and: continue] whileTrue: [
+ | iterationDone |
+ iterationDone:: actors createPromisePair.
+
+ (loopNode <-: neighbor: i) whenResolved: [:loopNeighbor |
+ (loopNeighbor <-: setParent: loopNode)
+ whenResolved: [:success |
+ success ifTrue: [
+ loopNeighbor == target ifTrue: [
+ master <-: done.
+ continue:: false
+ ] ifFalse: [
+ workQueue append: loopNeighbor ] ].
+ i:: i + 1.
+ iterationDone resolve: nil ] ].
+ iterationDone promise ].
+ outerIterationDone resolve: whileCompletionPromise ].
+ outerIterationDone promise ].
+
+ ^ outerLoopProm whenResolved: [:r |
+ [workQueue isEmpty] whileFalse: [
+ master <-: work: workQueue removeFirst target: target ] ]
+ )
+ )
+
+ public benchmark = (
+ | completionPP |
+ completionPP:: actors createPromisePair.
+ (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ actors snapshot: self.
+ ^ completionPP promise
+ )
+
+ public verifyResult: aBool = (
+ ^ aBool
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numWorkers: (problem at: 1) asInteger
+ gridSize: (problem at: 2) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ self todo.
+ run problemSize: '100:100'
+ )
+ )
+
+ public class NQueens numWorkers: workers size: size threshold: threshold = Benchmark <: Value (
+ | private numWorkers = workers.
+ private size = size.
+ private threshold = threshold.
+ |)(
+ public class Master new: completionRes = (
+ | private workers = Array new: numWorkers.
+ private messageCounter ::= 0.
+ private resultCounter ::= 0.
+ private numWorkSent ::= 0.
+ private numWorkCompleted ::= 0.
+ private completionRes = completionRes.
+ |
+ 1 to: numWorkers do: [:i |
+ workers at: i put: ((actors createActorFromValue: Worker) <-: new: self)].
+
+ send: (TransferArray new: 0) depth: 0.
+ actors snapshot: self.
+ )(
+ private send: arr depth: depth = (
+ messageCounter:: messageCounter + 1.
+ (workers at: messageCounter) <-: work: arr depth: depth.
+ messageCounter:: messageCounter % numWorkers.
+ numWorkSent:: numWorkSent + 1.
+ )
+
+ public work: arr depth: depth = (
+ send: arr depth: depth
+ )
+
+ public result = (
+ resultCounter:: resultCounter + 1.
+ )
+
+ public done = (
+ numWorkCompleted:: numWorkCompleted + 1.
+ numWorkCompleted = numWorkSent ifTrue: [
+ completionRes resolve: resultCounter
+ ]
+ )
+ )
+
+ public class Worker new: master = (
+ | private master = master. |
+ )(
+ public work: arr depth: depth = (
+ nqueensKernelPar: arr depth: depth.
+ master <-: done
+ )
+
+ private nqueensKernelPar: arr depth: depth = (
+ size = depth ifTrue: [
+ master <-: result.
+ ^ self
+ ].
+
+ depth >= threshold ifTrue: [
+ nqueensKernelSeq: arr depth: depth.
+ ^ self
+ ].
+
+ distributeWork: arr depth: depth
+ )
+
+ private distributeWork: arr depth: depth = (
+ | newDepth |
+ newDepth:: depth + 1.
+
+ 0 to: size - 1 do: [:i |
+ | b |
+ b:: TransferArray new: newDepth withAll: 0.
+ arr copyFrom: 1 to: depth to: b.
+ b at: newDepth put: i.
+ (board: b valid: newDepth) ifTrue: [
+ master <-: work: b depth: newDepth ] ]
+ )
+
+ private nqueensKernelSeq: arr depth: depth = (
+ | b newDepth |
+ size = depth ifTrue: [
+ master <-: result.
+ ^ self ].
+
+ newDepth:: depth + 1.
+ b:: TransferArray new: newDepth.
+ 0 to: size - 1 do: [:i |
+ arr copyFrom: 1 to: depth to: b.
+ b at: newDepth put: i.
+ (board: b valid: newDepth) ifTrue: [
+ nqueensKernelSeq: b depth: newDepth ] ]
+ )
+
+ private board: arr valid: n = (
+ 1 to: n do: [:i |
+ | p |
+ p:: arr at: i.
+ i + 1 to: n do: [:j |
+ | q |
+ q:: arr at: j.
+ (q = p or: [q = (p - (j - i)) or: [q = (p + (j - i))]]) ifTrue: [ ^ false ] ] ].
+ ^ true
+ )
+ )
+
+ public benchmark = (
+ | master completionPP |
+ completionPP:: actors createPromisePair.
+ master:: (actors createActorFromValue: Master) <-: new: completionPP resolver.
+ ^ completionPP promise
+ )
+
+ public verifyResult: result = (
+ size = 1 ifTrue: [ ^ result = 1 ].
+ size = 2 ifTrue: [ ^ result = 0 ].
+ size = 3 ifTrue: [ ^ result = 0 ].
+ size = 4 ifTrue: [ ^ result = 2 ].
+ size = 5 ifTrue: [ ^ result = 10 ].
+ size = 6 ifTrue: [ ^ result = 4 ].
+ size = 7 ifTrue: [ ^ result = 40 ].
+ size = 8 ifTrue: [ ^ result = 92 ].
+ size = 9 ifTrue: [ ^ result = 352 ].
+ size = 10 ifTrue: [ ^ result = 724 ].
+ size = 11 ifTrue: [ ^ result = 2680 ].
+ size = 12 ifTrue: [ ^ result = 14200 ].
+ size = 13 ifTrue: [ ^ result = 73712 ].
+ size = 14 ifTrue: [ ^ result = 365596 ].
+ size = 15 ifTrue: [ ^ result = 2279184 ].
+ size = 16 ifTrue: [ ^ result = 14772512 ].
+ size = 17 ifTrue: [ ^ result = 95815104 ].
+ size = 18 ifTrue: [ ^ result = 666090624 ].
+ size = 19 ifTrue: [ ^ result = 4968057848 ].
+ size = 20 ifTrue: [ ^ result = 39029188884 ].
+
+ ('---- result: ' + result asString + ' dont have a hardcoded verification result for this config yet') println.
+ ^ false
+ )
+ ) : (
+ public newInstance: problemSize = (
+ | problem |
+ problem:: problemSize split: ':'.
+ ^ self numWorkers: (problem at: 1) asInteger
+ size: (problem at: 2) asInteger
+ threshold: (problem at: 3) asInteger
+ )
+
+ public setupVerifiedRun: run = (
+ run problemSize: '20:12:4'
+ )
+ )
+)
diff --git a/som b/som
index 15ea6b57e..023a4925c 100755
--- a/som
+++ b/som
@@ -98,6 +98,8 @@ tools.add_argument('-as', '--actor-snapshots', help='actor tracing/replay with s
dest='actor_snapshots', action='store_true', default=False)
tools.add_argument('-sam', '--snapshot-all-messages', help='snapshot all messages',
dest='actor_snapshots_all', action='store_true', default=False)
+tools.add_argument('-sind', '--snapshot-inlining-depth', help='set the inlining limit of snapshot serialization',
+ dest='snapshot_inlining_depth', default=8)
tools.add_argument('-tas', '--test-snapshots', help='actor tracing with snapshots',
dest='test_actor_snapshots', action='store_true', default=False)
tools.add_argument('--coverage', help='determine SOMns code coverage and store in given file',
@@ -343,6 +345,8 @@ if args.actor_snapshots:
flags += ['-Dsom.actorSnapshot=true']
if args.actor_snapshots_all:
flags += ['-Dsom.actorSnapshotAll=true']
+if args.snapshot_inlining_depth:
+ flags += ['-Dsom.snapshotInliningDepth=%s' % args.snapshot_inlining_depth ]
if (args.truffle_profile or args.web_debugger or
args.dynamic_metrics or args.coverage or args.si_candidates):
diff --git a/src/som/Launcher.java b/src/som/Launcher.java
index fd9578e4e..5c6a954ce 100644
--- a/src/som/Launcher.java
+++ b/src/som/Launcher.java
@@ -13,6 +13,7 @@
import tools.concurrency.TracingActors.ReplayActor;
import tools.concurrency.TracingBackend;
import tools.snapshot.SnapshotBackend;
+import tools.snapshot.deserialization.SnapshotParser;
public final class Launcher {
@@ -29,6 +30,10 @@ public final class Launcher {
public static void main(final String[] args) {
StorageAccessor.initAccessors();
+ if (VmSettings.SNAPSHOT_REPLAY) {
+ SnapshotParser.preparations();
+ }
+
Builder builder = createContextBuilder(args);
Context context = builder.build();
@@ -41,7 +46,7 @@ public static void main(final String[] args) {
}
TracingBackend.waitForTrace();
- if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) {
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS && !VmSettings.REPLAY) {
SnapshotBackend.writeSnapshot();
}
diff --git a/src/som/VM.java b/src/som/VM.java
index b9d3c43ba..a49146f2b 100644
--- a/src/som/VM.java
+++ b/src/som/VM.java
@@ -316,7 +316,11 @@ public Object execute(final String selector) {
}
public int execute() {
- return objectSystem.executeApplication(vmMirror, mainActor);
+ if (VmSettings.SNAPSHOT_REPLAY) {
+ return objectSystem.executeApplicationFromSnapshot(vmMirror);
+ } else {
+ return objectSystem.executeApplication(vmMirror, mainActor);
+ }
}
public Actor getMainActor() {
diff --git a/src/som/compiler/MixinDefinition.java b/src/som/compiler/MixinDefinition.java
index 98ebef003..1d70d0f78 100644
--- a/src/som/compiler/MixinDefinition.java
+++ b/src/som/compiler/MixinDefinition.java
@@ -63,7 +63,7 @@
import som.vmobjects.SSymbol;
import tools.SourceCoordinate;
import tools.snapshot.nodes.AbstractSerializationNode;
-import tools.snapshot.nodes.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory;
+import tools.snapshot.nodes.ObjectSerializationNodes.ObjectSerializationNode;
import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.ClassSerializationNodeFactory;
@@ -210,23 +210,30 @@ public void initializeClass(final SClass result,
final Object superclassAndMixins, final boolean isTheValueClass,
final boolean isTheTransferObjectClass, final boolean isTheArrayClass) {
initializeClass(result, superclassAndMixins, isTheValueClass, isTheTransferObjectClass,
- isTheArrayClass, UninitializedObjectSerializationNodeFactory.getInstance());
+ isTheArrayClass, null, null);
}
public void initializeClass(final SClass result,
final Object superclassAndMixins,
- final NodeFactory extends AbstractSerializationNode> serializerFactory) {
- initializeClass(result, superclassAndMixins, false, false, false, serializerFactory);
+ final NodeFactory extends AbstractSerializationNode> serializerFactory,
+ final AbstractSerializationNode deserializer) {
+ initializeClass(result, superclassAndMixins, false, false, false, serializerFactory,
+ deserializer);
}
public void initializeClass(final SClass result,
final Object superclassAndMixins, final boolean isTheValueClass,
final boolean isTheTransferObjectClass, final boolean isTheArrayClass,
- final NodeFactory extends AbstractSerializationNode> serializerFactory) {
+ final NodeFactory extends AbstractSerializationNode> serializerFactory,
+ final AbstractSerializationNode deserializer) {
VM.callerNeedsToBeOptimized(
"This is supposed to result in a cacheable object, and thus is only the fallback case.");
ClassFactory factory = createClassFactory(superclassAndMixins,
- isTheValueClass, isTheTransferObjectClass, isTheArrayClass, serializerFactory);
+ isTheValueClass, isTheTransferObjectClass, isTheArrayClass);
+ if (serializerFactory != null) {
+ factory.customizeSerialization(serializerFactory, deserializer);
+ }
+
if (result.getSOMClass() != null) {
factory.getClassClassFactory().initializeClass(result.getSOMClass());
}
@@ -314,8 +321,7 @@ private ClassFactory getCached(final Object superclassAndMixins) {
public ClassFactory createClassFactory(final Object superclassAndMixins,
final boolean isTheValueClass, final boolean isTheTransferObjectClass,
- final boolean isTheArrayClass,
- final NodeFactory extends AbstractSerializationNode> serializerFactory) {
+ final boolean isTheArrayClass) {
CompilerAsserts.neverPartOfCompilation();
VM.callerNeedsToBeOptimized(
"This is supposed to result in a cacheable object, and thus is only the fallback case.");
@@ -366,13 +372,22 @@ public ClassFactory createClassFactory(final Object superclassAndMixins,
new SClass[] {Classes.classClass}, true,
// TODO: not passing a ClassFactory of the meta class here is incorrect,
// might not matter in practice
- null, ClassSerializationNodeFactory.getInstance());
+ null);
ClassFactory classFactory = new ClassFactory(name, this,
instanceSlots, dispatchables, instancesAreValues,
instancesAreTransferObjects, instancesAreArrays,
mixins, hasOnlyImmutableFields,
- classClassFactory, serializerFactory);
+ classClassFactory);
+
+ if (VmSettings.SNAPSHOTS_ENABLED) {
+ classClassFactory.customizeSerialization(
+ ClassSerializationNodeFactory.getInstance(),
+ ClassSerializationNodeFactory.create());
+ classFactory.customizeSerialization(
+ ObjectSerializationNode.getNodeFactory(classFactory),
+ ObjectSerializationNode.create(classFactory, 0));
+ }
cache.add(classFactory);
@@ -536,8 +551,7 @@ public SClass instantiateModuleClass() {
public SClass instantiateClass(final SObjectWithClass outer,
final Object superclassAndMixins) {
- ClassFactory factory = createClassFactory(superclassAndMixins,
- false, false, false, UninitializedObjectSerializationNodeFactory.getInstance());
+ ClassFactory factory = createClassFactory(superclassAndMixins, false, false, false);
return ClassInstantiationNode.instantiate(outer, factory, notAValue,
cannotBeValues);
}
@@ -886,7 +900,8 @@ public SSymbol getIdentifier() {
if (identifier == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
if (outer != null) {
- identifier = Symbols.symbolFor(outer.getIdentifier() + "." + this.name.getString());
+ identifier =
+ Symbols.symbolFor(outer.getIdentifier().getString() + "." + this.name.getString());
} else if (this.isModule && this.sourceSection != null) {
Path absolute = Paths.get(this.sourceSection.getSource().getURI());
Path relative =
diff --git a/src/som/interpreter/Types.java b/src/som/interpreter/Types.java
index 5dad36b13..aac18fad1 100644
--- a/src/som/interpreter/Types.java
+++ b/src/som/interpreter/Types.java
@@ -32,6 +32,7 @@
import som.VM;
import som.interpreter.actors.SFarReference;
import som.interpreter.actors.SPromise;
+import som.primitives.ObjectPrims.ClassPrim;
import som.primitives.SizeAndLengthPrim;
import som.primitives.SizeAndLengthPrimFactory;
import som.primitives.threading.TaskThreads.SomForkJoinTask;
@@ -67,6 +68,10 @@
Object[].class}) // Object[] is only for argument passing
public class Types {
+ /**
+ * This is a helper method to get the SClass of an object.
+ * If an optimized implementation is needed use {@link ClassPrim}.
+ */
public static SClass getClassOf(final Object obj) {
VM.callerNeedsToBeOptimized("If this is reached on a fast path, it indicates "
+ "that it doesn't use the correct nodes or unoptimized code");
diff --git a/src/som/interpreter/actors/AbstractPromiseResolutionNode.java b/src/som/interpreter/actors/AbstractPromiseResolutionNode.java
index 1a413d944..714c70e76 100644
--- a/src/som/interpreter/actors/AbstractPromiseResolutionNode.java
+++ b/src/som/interpreter/actors/AbstractPromiseResolutionNode.java
@@ -78,6 +78,11 @@ public SResolver selfResolution(final SResolver resolver,
public SResolver chainedPromise(final VirtualFrame frame,
final SResolver resolver, final SPromise promiseValue,
final boolean haltOnResolver, final boolean haltOnResolution) {
+
+ if (VmSettings.SNAPSHOT_REPLAY && promiseValue.hasChainedPromise(resolver.getPromise())) {
+ return resolver;
+ }
+
chainPromise(resolver, promiseValue, haltOnResolver, haltOnResolution);
return resolver;
}
diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java
index 208c0dda6..ed16085a0 100644
--- a/src/som/interpreter/actors/Actor.java
+++ b/src/som/interpreter/actors/Actor.java
@@ -15,6 +15,7 @@
import som.VM;
import som.interpreter.SomLanguage;
+import som.interpreter.actors.EventualMessage.PromiseMessage;
import som.interpreter.objectstorage.ObjectTransitionSafepoint;
import som.primitives.ObjectPrims.IsValue;
import som.vm.Activity;
@@ -33,7 +34,7 @@
import tools.debugger.entities.DynamicScopeType;
import tools.replay.actors.ActorExecutionTrace;
import tools.replay.nodes.TraceActorContextNode;
-import tools.snapshot.SnapshotBuffer;
+import tools.snapshot.deserialization.SnapshotParser;
/**
@@ -65,7 +66,7 @@ public static void initializeActorSystem(final SomLanguage lang) {
public static Actor createActor(final VM vm) {
if (VmSettings.REPLAY || VmSettings.KOMPOS_TRACING) {
return new ReplayActor(vm);
- } else if (VmSettings.ACTOR_TRACING) {
+ } else if (VmSettings.USE_TRACING_ACTORS) {
return new TracingActor(vm);
} else {
return new Actor(vm);
@@ -176,6 +177,32 @@ public synchronized void sendInitialStartMessage(final EventualMessage msg,
doSend(msg, pool);
}
+ public synchronized void sendSnapshotMessage(final EventualMessage msg) {
+ if (msg instanceof PromiseMessage) {
+ if (!SnapshotParser.addPMsg(msg)) {
+ // avoid duplicate promise messages
+ return;
+ }
+ }
+
+ if (firstMessage != null) {
+ appendToMailbox(msg);
+ } else {
+ firstMessage = msg;
+ }
+ }
+
+ public synchronized void executeIfNecessarry(final ForkJoinPool actorPool) {
+ if (firstMessage == null) {
+ return;
+ }
+
+ if (!isExecuting) {
+ isExecuting = true;
+ execute(actorPool);
+ }
+ }
+
private void doSend(final EventualMessage msg,
final ForkJoinPool actorPool) {
assert msg.getTarget() == this;
@@ -253,7 +280,7 @@ void doRun() {
t.currentlyExecutingActor = actor;
if (VmSettings.ACTOR_TRACING) {
- ActorExecutionTrace.recordActorContext((TracingActor) actor, tracer);
+ ActorExecutionTrace.recordActorContext((TracingActor) actor, t, tracer);
} else if (VmSettings.KOMPOS_TRACING) {
KomposTrace.currentActivity(actor);
}
@@ -276,18 +303,10 @@ protected void processCurrentMessages(final ActorProcessingThread currentThread,
final WebDebugger dbg) {
assert size > 0;
- if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) {
- SnapshotBuffer sb = currentThread.getSnapshotBuffer();
- sb.getRecord().handleTodos(sb);
- firstMessage.serialize(sb);
- }
execute(firstMessage, currentThread, dbg);
if (size > 1) {
for (EventualMessage msg : mailboxExtension) {
- if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) {
- msg.serialize(currentThread.getSnapshotBuffer());
- }
execute(msg, currentThread, dbg);
}
}
@@ -338,6 +357,10 @@ private boolean getCurrentMessagesOrCompleteExecution() {
return true;
}
+
+ public TraceActorContextNode getActorContextNode() {
+ return tracer;
+ }
}
@TruffleBoundary
@@ -380,6 +403,10 @@ public Activity getActivity() {
public Actor getCurrentActor() {
return currentlyExecutingActor;
}
+
+ public void setCurrentActorForSnapshot(final Actor current) {
+ this.currentlyExecutingActor = current;
+ }
}
@Override
diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java
index 994cea940..eb02c1af9 100644
--- a/src/som/interpreter/actors/EventualMessage.java
+++ b/src/som/interpreter/actors/EventualMessage.java
@@ -3,6 +3,7 @@
import java.util.Arrays;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.source.SourceSection;
@@ -79,16 +80,16 @@ public SourceSection getTargetSourceSection() {
return onReceive.getRootNode().getSourceSection();
}
- public long serialize(final SnapshotBuffer sb) {
- ReceivedRootNode rm = (ReceivedRootNode) this.onReceive.getRootNode();
-
- if (sb.needsToBeSnapshot(getMessageId())) {
- // Not sure if this is optimized, worst case need to duplicate this for all messages
- return rm.getSerializer().execute(this, sb);
- } else {
- // need to be careful, might interfere with promise serialization...
- return -1;
+ @TruffleBoundary
+ // TODO: can we establish a structure for this? at the moment, we have an
+ // indirection here, which leads us to a serializer that's not compilation
+ // final, I think
+ public long forceSerialize(final SnapshotBuffer sb) {
+ if (sb.getRecord().containsObject(this)) {
+ return sb.getRecord().getObjectPointer(this);
}
+ ReceivedRootNode rm = (ReceivedRootNode) this.onReceive.getRootNode();
+ return rm.getSerializer().execute(this, sb);
}
/**
@@ -112,7 +113,7 @@ protected AbstractDirectMessage(final Actor target, final SSymbol selector,
this.sender = sender;
this.target = target;
- if (VmSettings.SNAPSHOTS_ENABLED) {
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
this.messageId = ActorProcessingThread.currentThread().getSnapshotId();
}
@@ -133,7 +134,7 @@ protected AbstractDirectMessage(final Actor target, final SSymbol selector,
this.sender = sender;
this.target = target;
- if (VmSettings.SNAPSHOTS_ENABLED) {
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
this.messageId = SnapshotBackend.getSnapshotVersion();
}
@@ -238,7 +239,7 @@ public PromiseMessage(final Object[] arguments, final Actor originalSender,
triggerPromiseResolverBreakpoint);
this.originalSender = originalSender;
- if (VmSettings.SNAPSHOTS_ENABLED) {
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
this.messageId = ActorProcessingThread.currentThread().getSnapshotId();
}
}
@@ -258,6 +259,8 @@ public final Actor getSender() {
*/
public abstract void setPromise(SPromise promise);
+ public abstract boolean isDelivered();
+
@Override
public boolean getHaltOnPromiseMessageResolution() {
return getPromise().getHaltOnResolution();
@@ -284,6 +287,9 @@ protected AbstractPromiseSendMessage(final SSymbol selector,
this.selector = selector;
assert (args[0] instanceof SPromise);
this.originalTarget = (SPromise) args[0];
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
+ this.messageId = ActorProcessingThread.currentThread().getSnapshotId();
+ }
}
@Override
@@ -301,9 +307,9 @@ private void determineAndSetTarget(final Object rcvr, final Actor target,
this.target = finalTarget; // for sends to far references, we need to adjust the target
this.finalSender = sendingActor;
- if (VmSettings.SNAPSHOTS_ENABLED) {
- this.messageId = Math.min(this.messageId,
- ActorProcessingThread.currentThread().getSnapshotId());
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
+ this.messageId =
+ Math.max(ActorProcessingThread.currentThread().getSnapshotId(), this.messageId);
}
}
@@ -318,6 +324,7 @@ public Actor getTarget() {
return target;
}
+ @Override
public boolean isDelivered() {
return target != null;
}
@@ -342,7 +349,7 @@ public SPromise getPromise() {
@Override
public final void setPromise(final SPromise promise) {
assert VmSettings.SNAPSHOTS_ENABLED;
- assert promise != null && originalTarget == null;
+ assert promise != null;
this.originalTarget = promise;
}
@@ -375,6 +382,9 @@ protected AbstractPromiseCallbackMessage(final Actor owner, final SBlock callbac
super(new Object[] {callback, null}, owner, resolver, onReceive,
triggerMessageReceiverBreakpoint, triggerPromiseResolverBreakpoint);
this.promise = promiseRegisteredOn;
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
+ this.messageId = ActorProcessingThread.currentThread().getSnapshotId();
+ }
}
@Override
@@ -390,9 +400,8 @@ public void resolve(final Object rcvr, final Actor target, final Actor sendingAc
*/
private void setPromiseValue(final Object value, final Actor resolvingActor) {
args[1] = originalSender.wrapForUse(value, resolvingActor, null);
- if (VmSettings.SNAPSHOTS_ENABLED) {
- this.messageId = Math.min(this.messageId,
- ActorProcessingThread.currentThread().getSnapshotId());
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
+ this.messageId = ActorProcessingThread.currentThread().getSnapshotId();
}
}
@@ -426,6 +435,11 @@ public final void setPromise(final SPromise promise) {
assert promise != null && this.promise == null;
this.promise = promise;
}
+
+ @Override
+ public boolean isDelivered() {
+ return originalSender != null;
+ }
}
public static final class PromiseCallbackMessage extends AbstractPromiseCallbackMessage {
diff --git a/src/som/interpreter/actors/ReceivedMessage.java b/src/som/interpreter/actors/ReceivedMessage.java
index 163929ece..f1c3b6202 100644
--- a/src/som/interpreter/actors/ReceivedMessage.java
+++ b/src/som/interpreter/actors/ReceivedMessage.java
@@ -23,7 +23,7 @@ public class ReceivedMessage extends ReceivedRootNode {
public ReceivedMessage(final AbstractMessageSendNode onReceive,
final SSymbol selector, final SomLanguage lang) {
- super(lang, onReceive.getSourceSection(), null);
+ super(lang, onReceive.getSourceSection(), null, selector);
this.onReceive = onReceive;
this.selector = selector;
assert onReceive.getSourceSection() != null;
@@ -74,9 +74,9 @@ private void resolveFuture(final Object result) {
public static final class ReceivedCallback extends ReceivedRootNode {
@Child protected DirectCallNode onReceive;
- public ReceivedCallback(final RootCallTarget onReceive) {
+ public ReceivedCallback(final RootCallTarget onReceive, final SSymbol selector) {
super(SomLanguage.getLanguage(onReceive.getRootNode()),
- onReceive.getRootNode().getSourceSection(), null);
+ onReceive.getRootNode().getSourceSection(), null, selector);
this.onReceive = Truffle.getRuntime().createDirectCallNode(onReceive);
}
diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java
index 94fdf661a..e1847292d 100644
--- a/src/som/interpreter/actors/ReceivedRootNode.java
+++ b/src/som/interpreter/actors/ReceivedRootNode.java
@@ -4,18 +4,28 @@
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.profiles.ValueProfile;
import com.oracle.truffle.api.source.SourceSection;
import som.VM;
import som.interpreter.SArguments;
import som.interpreter.SomLanguage;
+import som.interpreter.actors.Actor.ActorProcessingThread;
+import som.interpreter.actors.EventualMessage.PromiseSendMessage;
import som.interpreter.actors.SPromise.SResolver;
+import som.primitives.ObjectPrims.ClassPrim;
+import som.primitives.ObjectPrimsFactory.ClassPrimFactory;
import som.vm.VmSettings;
+import som.vmobjects.SSymbol;
import tools.concurrency.KomposTrace;
+import tools.concurrency.TracingActors.TracingActor;
import tools.debugger.WebDebugger;
import tools.debugger.entities.DynamicScopeType;
import tools.replay.nodes.TraceMessageNode;
import tools.replay.nodes.TraceMessageNodeGen;
+import tools.snapshot.SnapshotBackend;
+import tools.snapshot.SnapshotBuffer;
+import tools.snapshot.SnapshotRecord;
import tools.snapshot.nodes.MessageSerializationNode;
import tools.snapshot.nodes.MessageSerializationNodeFactory;
@@ -28,12 +38,17 @@ public abstract class ReceivedRootNode extends RootNode {
@Child protected TraceMessageNode msgTracer = TraceMessageNodeGen.create();
@Child protected MessageSerializationNode serializer;
+ @Child protected ClassPrim classPrim;
+
private final VM vm;
protected final WebDebugger dbg;
private final SourceSection sourceSection;
+ private final ValueProfile msgClass;
+
protected ReceivedRootNode(final SomLanguage language,
- final SourceSection sourceSection, final FrameDescriptor frameDescriptor) {
+ final SourceSection sourceSection, final FrameDescriptor frameDescriptor,
+ final SSymbol selector) {
super(language, frameDescriptor);
assert sourceSection != null;
this.vm = language.getVM();
@@ -44,9 +59,13 @@ protected ReceivedRootNode(final SomLanguage language,
}
this.sourceSection = sourceSection;
if (VmSettings.SNAPSHOTS_ENABLED) {
- serializer = MessageSerializationNodeFactory.create();
+ serializer = MessageSerializationNodeFactory.create(selector);
+ classPrim = ClassPrimFactory.create(null);
+ msgClass = ValueProfile.createClassProfile();
} else {
serializer = null;
+ classPrim = null;
+ msgClass = null;
}
}
@@ -57,6 +76,22 @@ protected abstract Object executeBody(VirtualFrame frame, EventualMessage msg,
public final Object execute(final VirtualFrame frame) {
EventualMessage msg = (EventualMessage) SArguments.rcvr(frame);
+ ActorProcessingThread currentThread = (ActorProcessingThread) Thread.currentThread();
+
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS && !VmSettings.REPLAY) {
+ SnapshotBuffer sb = currentThread.getSnapshotBuffer();
+ sb.getRecord().resetRecordifNecessary(currentThread.getSnapshotId());
+ sb.getRecord().handleObjectsReferencedFromFarRefs(sb, classPrim);
+
+ if (sb.needsToBeSnapshot(msg.getMessageId())) {
+ long msgIdentifier =
+ ((TracingActor) msgClass.profile(msg).getTarget()).getSnapshotRecord()
+ .getMessageIdentifier();
+ long location = serializeMessageIfNecessary(msg, sb);
+ sb.getOwner().addMessageLocation(msgIdentifier, location);
+ }
+ }
+
boolean haltOnResolver;
boolean haltOnResolution;
@@ -84,6 +119,21 @@ public final Object execute(final VirtualFrame frame) {
msgTracer.execute(msg);
}
+ // this has to be after the msgTracing, as otherwise we will expect messages we shoudln't
+ // expect.
+ // also the getSnapshotbuffer is necessary as it will be a new one.
+ if (VmSettings.SNAPSHOTS_ENABLED) {
+ if (msg.getResolver() != null
+ && currentThread.getSnapshotId() != SnapshotBackend.getSnapshotVersion()) {
+ // Snapshot was trigged while executing this message
+ // we need to serialize the promise and mark it.
+ SnapshotBuffer sb = currentThread.getSnapshotBuffer();
+ SResolver resolver = msg.getResolver();
+
+ SnapshotBackend.registerLostResolution(resolver, sb);
+ }
+ }
+
if (VmSettings.KOMPOS_TRACING) {
KomposTrace.scopeEnd(DynamicScopeType.TURN);
}
@@ -95,6 +145,26 @@ public SourceSection getSourceSection() {
return sourceSection;
}
+ private long serializeMessageIfNecessary(final EventualMessage msg,
+ final SnapshotBuffer sb) {
+
+ if (sb.getRecord().containsObject(msg)) {
+ // location already known
+ return sb.getRecord().getObjectPointer(msg);
+ } else if (msg instanceof PromiseSendMessage) {
+ // check promise owner
+ PromiseSendMessage pm = (PromiseSendMessage) msg;
+ SnapshotRecord sr = ((TracingActor) pm.getPromise().getOwner()).getSnapshotRecord();
+ if (sr.containsObject(msg)) {
+ // location known by promise owner
+ return sr.getObjectPointer(msg);
+ }
+ }
+
+ // message wasn't serialized before
+ return sb.calculateReference(serializer.execute(msg, sb));
+ }
+
protected final void resolvePromise(final VirtualFrame frame,
final SResolver resolver, final Object result,
final boolean haltOnResolver, final boolean haltOnResolution) {
diff --git a/src/som/interpreter/actors/SFarReference.java b/src/som/interpreter/actors/SFarReference.java
index e43701a8b..acdeae807 100644
--- a/src/som/interpreter/actors/SFarReference.java
+++ b/src/som/interpreter/actors/SFarReference.java
@@ -2,7 +2,6 @@
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import som.interpreter.objectstorage.ClassFactory;
import som.vm.VmSettings;
import som.vmobjects.SAbstractObject;
import som.vmobjects.SClass;
@@ -38,6 +37,10 @@ public SClass getSOMClass() {
return farReferenceClass;
}
+ public static SClass getFarRefClass() {
+ return farReferenceClass;
+ }
+
@Override
public String toString() {
return "FarRef[" + value.toString() + ", " + actor.toString() + "]";
@@ -52,8 +55,9 @@ public static void setSOMClass(final SClass cls) {
assert farReferenceClass == null || cls == null;
farReferenceClass = cls;
if (VmSettings.SNAPSHOTS_ENABLED) {
- ClassFactory group = farReferenceClass.getInstanceFactory();
- group.getSerializer().replace(FarRefSerializationNodeFactory.create(group));
+ cls.customizeSerializerFactory(
+ FarRefSerializationNodeFactory.getInstance(),
+ FarRefSerializationNodeFactory.create());
}
}
}
diff --git a/src/som/interpreter/actors/SPromise.java b/src/som/interpreter/actors/SPromise.java
index 074ef4d86..a74d6f801 100644
--- a/src/som/interpreter/actors/SPromise.java
+++ b/src/som/interpreter/actors/SPromise.java
@@ -9,7 +9,6 @@
import com.oracle.truffle.api.source.SourceSection;
import som.interpreter.actors.EventualMessage.PromiseMessage;
-import som.interpreter.objectstorage.ClassFactory;
import som.vm.VmSettings;
import som.vmobjects.SClass;
import som.vmobjects.SObjectWithClass;
@@ -33,7 +32,7 @@ public static SPromise createPromise(final Actor owner,
final SourceSection section) {
if (VmSettings.KOMPOS_TRACING) {
return new SMedeorPromise(owner, haltOnResolver, haltOnResolution, section);
- } else if (VmSettings.ACTOR_TRACING || VmSettings.REPLAY) {
+ } else if (VmSettings.USE_TRACING_ACTORS || VmSettings.REPLAY) {
return new STracingPromise(owner, haltOnResolver, haltOnResolution);
} else {
return new SPromise(owner, haltOnResolver, haltOnResolution);
@@ -130,6 +129,40 @@ public final void setValueFromSnapshot(final Object value) {
this.value = value;
}
+ public final void resolveFromSnapshot(final Object value, final Resolution resolutionState,
+ final Actor resolver, final boolean schedule) {
+ assert value != null;
+ this.value = owner.wrapForUse(value, resolver, null);
+ this.resolutionState = resolutionState;
+
+ if (schedule) {
+ if (resolutionState == Resolution.SUCCESSFUL) {
+ SResolver.scheduleAllWhenResolvedSnapshot(this, value, resolver);
+ } else {
+ assert resolutionState == Resolution.ERRONEOUS;
+ SResolver.scheduleAllOnErrorSnapshot(this, value, resolver);
+ }
+ // resolveChainedPromisesUnsync(resolutionState, this, resolver, current, actorPool,
+ // haltOnResolution,
+ // whenResolvedProfile);
+ }
+ }
+
+ public final void unresolveFromSnapshot(final Resolution resolutionState) {
+ this.value = null;
+ this.resolutionState = resolutionState;
+
+ if (chainedPromise != null) {
+ chainedPromise.unresolveFromSnapshot(Resolution.CHAINED);
+
+ if (chainedPromiseExt != null) {
+ for (SPromise prom : chainedPromiseExt) {
+ prom.unresolveFromSnapshot(Resolution.CHAINED);
+ }
+ }
+ }
+ }
+
public long getPromiseId() {
return 0;
}
@@ -142,8 +175,9 @@ public static void setSOMClass(final SClass cls) {
assert promiseClass == null || cls == null;
promiseClass = cls;
if (VmSettings.SNAPSHOTS_ENABLED) {
- ClassFactory group = promiseClass.getInstanceFactory();
- group.getSerializer().replace(PromiseSerializationNodeFactory.create(group));
+ promiseClass.customizeSerializerFactory(
+ PromiseSerializationNodeFactory.getInstance(),
+ PromiseSerializationNodeFactory.create());
}
}
@@ -161,8 +195,9 @@ public final synchronized SPromise getChainedPromiseFor(final Actor target) {
if (isCompleted()) {
remote.value = value;
remote.resolutionState = resolutionState;
- if (VmSettings.ACTOR_TRACING || VmSettings.REPLAY) {
- ((STracingPromise) remote).resolvingActor = ((STracingPromise) this).resolvingActor;
+ if (VmSettings.USE_TRACING_ACTORS || VmSettings.REPLAY) {
+ ((STracingPromise) remote).setResolvingActorForSnapshot(
+ ((STracingPromise) this).resolvingActor);
}
} else {
addChainedPromise(remote);
@@ -220,6 +255,14 @@ protected final void scheduleCallbacksOnResolution(final Object result,
msg.getTarget().send(msg, actorPool);
}
+ protected final void scheduleCallbacksSnapshot(final Object result,
+ final PromiseMessage msg, final Actor current) {
+
+ assert owner != null;
+ msg.resolve(result, owner, current);
+ msg.getTarget().sendSnapshotMessage(msg);
+ }
+
public final synchronized void addChainedPromise(final SPromise remote) {
assert remote != null;
remote.resolutionState = Resolution.CHAINED;
@@ -255,6 +298,10 @@ public final Resolution getResolutionStateUnsync() {
}
public final boolean assertNotCompleted() {
+ if (VmSettings.SNAPSHOT_REPLAY) {
+ this.unresolveFromSnapshot(Resolution.UNRESOLVED);
+ }
+
assert !isCompleted() : "Not sure yet what to do with re-resolving of promises? just ignore it? Error?";
assert value == null : "If it isn't resolved yet, it shouldn't have a value";
return true;
@@ -311,6 +358,15 @@ public ArrayList getChainedPromiseExtUnsync() {
return chainedPromiseExt;
}
+ public boolean hasChainedPromise(final SPromise prom) {
+ if (this.chainedPromise == prom) {
+ return true;
+ } else if (this.chainedPromiseExt != null) {
+ return chainedPromiseExt.contains(prom);
+ }
+ return false;
+ }
+
public static class STracingPromise extends SPromise {
protected STracingPromise(final Actor owner, final boolean haltOnResolver,
@@ -327,6 +383,9 @@ public int getResolvingActor() {
return resolvingActor;
}
+ public void setResolvingActorForSnapshot(final int resolver) {
+ this.resolvingActor = resolver;
+ }
}
public static final class SMedeorPromise extends STracingPromise {
@@ -380,8 +439,9 @@ public static void setSOMClass(final SClass cls) {
resolverClass = cls;
if (VmSettings.SNAPSHOTS_ENABLED) {
- ClassFactory group = resolverClass.getInstanceFactory();
- group.getSerializer().replace(ResolverSerializationNodeFactory.create(group));
+ resolverClass.customizeSerializerFactory(
+ ResolverSerializationNodeFactory.getInstance(),
+ ResolverSerializationNodeFactory.create());
}
}
@@ -449,9 +509,9 @@ protected static void resolveAndTriggerListenersUnsynced(final Resolution type,
final ValueProfile whenResolvedProfile) {
assert !(result instanceof SPromise);
- if (VmSettings.ACTOR_TRACING || VmSettings.REPLAY) {
- ((STracingPromise) p).resolvingActor =
- ((TracingActor) EventualMessage.getActorCurrentMessageIsExecutionOn()).getActorId();
+ if (VmSettings.USE_TRACING_ACTORS || VmSettings.REPLAY) {
+ ((STracingPromise) p).setResolvingActorForSnapshot(
+ ((TracingActor) EventualMessage.getActorCurrentMessageIsExecutionOn()).getActorId());
} else if (VmSettings.KOMPOS_TRACING) {
if (type == Resolution.SUCCESSFUL && p.resolutionState != Resolution.CHAINED) {
KomposTrace.promiseResolution(p.getPromiseId(), result);
@@ -536,6 +596,43 @@ protected static void scheduleAllOnErrorUnsync(final SPromise promise,
actorPool, haltOnResolution);
}
}
+
+ /**
+ * Schedule all whenResolved callbacks for the promise.
+ */
+ protected static void scheduleAllWhenResolvedSnapshot(final SPromise promise,
+ final Object result, final Actor current) {
+ if (promise.whenResolved != null) {
+ promise.scheduleCallbacksSnapshot(result, promise.whenResolved, current);
+ scheduleExtensionsSnapshot(promise, promise.whenResolvedExt, result, current);
+ }
+ }
+
+ /**
+ * Schedule callbacks from the whenResolvedExt extension array.
+ */
+ @TruffleBoundary
+ private static void scheduleExtensionsSnapshot(final SPromise promise,
+ final ArrayList extension,
+ final Object result, final Actor current) {
+ if (extension != null) {
+ for (int i = 0; i < extension.size(); i++) {
+ PromiseMessage callbackOrMsg = extension.get(i);
+ promise.scheduleCallbacksSnapshot(result, callbackOrMsg, current);
+ }
+ }
+ }
+
+ /**
+ * Schedule all onError callbacks for the promise.
+ */
+ protected static void scheduleAllOnErrorSnapshot(final SPromise promise,
+ final Object result, final Actor current) {
+ if (promise.onError != null) {
+ promise.scheduleCallbacksSnapshot(result, promise.onError, current);
+ scheduleExtensionsSnapshot(promise, promise.onErrorExt, result, current);
+ }
+ }
}
@CompilationFinal public static SClass pairClass;
diff --git a/src/som/interpreter/nodes/InstantiationNode.java b/src/som/interpreter/nodes/InstantiationNode.java
index 7c5143513..993394875 100644
--- a/src/som/interpreter/nodes/InstantiationNode.java
+++ b/src/som/interpreter/nodes/InstantiationNode.java
@@ -11,7 +11,6 @@
import som.vm.constants.Classes;
import som.vmobjects.SClass;
import som.vmobjects.SObjectWithClass;
-import tools.snapshot.nodes.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory;
public abstract class InstantiationNode extends Node {
@@ -31,8 +30,7 @@ protected InstantiationNode(final MixinDefinition mixinDef) {
}
protected final ClassFactory createClassFactory(final Object superclassAndMixins) {
- return mixinDefinition.createClassFactory(superclassAndMixins, false, false, false,
- UninitializedObjectSerializationNodeFactory.getInstance());
+ return mixinDefinition.createClassFactory(superclassAndMixins, false, false, false);
}
protected boolean sameSuperAndMixins(final Object superclassAndMixins, final Object cached) {
diff --git a/src/som/interpreter/nodes/dispatch/DispatchGuard.java b/src/som/interpreter/nodes/dispatch/DispatchGuard.java
index 6b5129b3b..6533deee4 100644
--- a/src/som/interpreter/nodes/dispatch/DispatchGuard.java
+++ b/src/som/interpreter/nodes/dispatch/DispatchGuard.java
@@ -1,5 +1,7 @@
package som.interpreter.nodes.dispatch;
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import som.compiler.MixinDefinition.SlotDefinition;
@@ -14,9 +16,16 @@
public abstract class DispatchGuard {
+ private static final Assumption alwaysValidAssumption =
+ Truffle.getRuntime().createAssumption("Global Guard Assumption, always valid");
+
public abstract boolean entryMatches(Object obj)
throws InvalidAssumptionException;
+ public Assumption getAssumption() {
+ return alwaysValidAssumption;
+ }
+
public static DispatchGuard create(final Object obj) {
if (obj == Boolean.TRUE) {
return new CheckTrue();
@@ -55,6 +64,14 @@ public static CheckSObject createSObjectCheck(final SObject obj) {
return new CheckSImmutableObject(((SImmutableObject) obj).getObjectLayout());
}
+ public static CheckSObject createSObjectCheck(final ClassFactory factory) {
+ if (factory.hasOnlyImmutableFields()) {
+ return new CheckSImmutableObject(factory.getInstanceLayout());
+ } else {
+ return new CheckSMutableObject(factory.getInstanceLayout());
+ }
+ }
+
private static final class CheckClass extends DispatchGuard {
private final Class> expected;
@@ -141,6 +158,11 @@ public boolean entryMatches(final Object obj) throws InvalidAssumptionException
((SMutableObject) obj).getObjectLayout() == expected;
}
+ @Override
+ public Assumption getAssumption() {
+ return expected.getIsLatestAssumption();
+ }
+
@Override
public SObject cast(final Object obj) {
return (SMutableObject) obj;
@@ -160,6 +182,11 @@ public boolean entryMatches(final Object obj) throws InvalidAssumptionException
((SImmutableObject) obj).getObjectLayout() == expected;
}
+ @Override
+ public Assumption getAssumption() {
+ return expected.getIsLatestAssumption();
+ }
+
@Override
public SObject cast(final Object obj) {
return (SImmutableObject) obj;
diff --git a/src/som/interpreter/objectstorage/ClassFactory.java b/src/som/interpreter/objectstorage/ClassFactory.java
index 639764042..6a74478b3 100644
--- a/src/som/interpreter/objectstorage/ClassFactory.java
+++ b/src/som/interpreter/objectstorage/ClassFactory.java
@@ -1,9 +1,12 @@
package som.interpreter.objectstorage;
+import java.util.concurrent.atomic.AtomicInteger;
+
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.NodeFactory;
import som.VM;
@@ -14,6 +17,8 @@
import som.vm.VmSettings;
import som.vmobjects.SClass;
import som.vmobjects.SSymbol;
+import tools.snapshot.SnapshotBuffer;
+import tools.snapshot.deserialization.DeserializationBuffer;
import tools.snapshot.nodes.AbstractSerializationNode;
import tools.snapshot.nodes.SerializerRootNode;
@@ -61,7 +66,10 @@ public final class ClassFactory {
private final ClassFactory classClassFactory;
- protected final SerializerRootNode serializationRoot;
+ protected final AtomicInteger identityGen;
+
+ @CompilationFinal private NodeFactory extends AbstractSerializationNode> serializerFactory;
+ @CompilationFinal private AbstractSerializationNode deserializer;
public ClassFactory(final SSymbol name, final MixinDefinition mixinDef,
final EconomicSet instanceSlots,
@@ -71,8 +79,7 @@ public ClassFactory(final SSymbol name, final MixinDefinition mixinDef,
final boolean isArray,
final SClass[] superclassAndMixins,
final boolean hasOnlyImmutableFields,
- final ClassFactory classClassFactory,
- final NodeFactory extends AbstractSerializationNode> serializerFactory) {
+ final ClassFactory classClassFactory) {
assert instanceSlots == null || instanceSlots.size() > 0;
this.className = name;
@@ -88,10 +95,9 @@ public ClassFactory(final SSymbol name, final MixinDefinition mixinDef,
this.superclassAndMixins = superclassAndMixins;
if (VmSettings.SNAPSHOTS_ENABLED) {
- this.serializationRoot =
- new SerializerRootNode(serializerFactory.createNode(this));
+ this.identityGen = new AtomicInteger(0);
} else {
- this.serializationRoot = null;
+ this.identityGen = null;
}
VM.callerNeedsToBeOptimized(
@@ -135,14 +141,38 @@ public SClass[] getSuperclassAndMixins() {
return superclassAndMixins;
}
- public AbstractSerializationNode getSerializer() {
- return serializationRoot.getSerializer();
- }
-
public MixinDefinition getMixinDefinition() {
return mixinDef;
}
+ public NodeFactory extends AbstractSerializationNode> getSerializerFactory() {
+ // serializerFactory can be null for Classes.messageClass
+ return serializerFactory;
+ }
+
+ public void customizeSerialization(
+ final NodeFactory extends AbstractSerializationNode> factory,
+ final AbstractSerializationNode deserializer) {
+ serializerFactory = factory;
+ new SerializerRootNode(deserializer);
+ // this is needed since the deserialize is currently
+ // still used for serialization, and some classes do
+ // rewriting with `replace()`
+
+ this.deserializer = deserializer;
+ }
+
+ public Object deserialize(final DeserializationBuffer bb, final SClass clazz) {
+ return deserializer.deserialize(bb, clazz);
+ }
+
+ public void serialize(final Object o, final SnapshotBuffer sb) {
+ VM.callerNeedsToBeOptimized("This serialize method should not be used from PEed code.");
+ // TODO: we are using the deserialize node here. The deserializer and serializer should be
+ // split. This would also allow us to optimize deserialization.
+ deserializer.execute(o, sb);
+ }
+
/**
* This method is used to verify whether the class identified by `mixinId` was
* created from either the superclasses or any of other mixins encapsulated by
@@ -200,4 +230,11 @@ public String toString() {
public SSymbol getIdentifier() {
return mixinDef.getIdentifier();
}
+
+ public int createIdentity() {
+ if (mixinDef == null) {
+ return (className.getSymbolId() << 16) | identityGen.getAndIncrement();
+ }
+ return (mixinDef.getIdentifier().getSymbolId() << 16) | identityGen.getAndIncrement();
+ }
}
diff --git a/src/som/interpreter/objectstorage/InitializerFieldWrite.java b/src/som/interpreter/objectstorage/InitializerFieldWrite.java
index a75ca087c..babb76c91 100644
--- a/src/som/interpreter/objectstorage/InitializerFieldWrite.java
+++ b/src/som/interpreter/objectstorage/InitializerFieldWrite.java
@@ -99,7 +99,7 @@ protected final IntValueProfile createProfile() {
public final long longValueSet(final SImmutableObject rcvr, final long value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getLongAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -114,7 +114,7 @@ public final long longValueSet(final SImmutableObject rcvr, final long value,
public final long longValueSetOrUnset(final SImmutableObject rcvr, final long value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getLongAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
accessor.markPrimAsSet(rcvr, primMarkProfile);
@@ -131,7 +131,7 @@ public final long longValueSetOrUnset(final SImmutableObject rcvr, final long va
public final long longValueSet(final SMutableObject rcvr, final long value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getLongAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -146,7 +146,7 @@ public final long longValueSet(final SMutableObject rcvr, final long value,
public final long longValueSetOrUnset(final SMutableObject rcvr, final long value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getLongAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
accessor.markPrimAsSet(rcvr, primMarkProfile);
@@ -162,7 +162,7 @@ public final long longValueSetOrUnset(final SMutableObject rcvr, final long valu
public final double doubleValueSet(final SMutableObject rcvr, final double value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getDoubleAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -177,7 +177,7 @@ public final double doubleValueSet(final SMutableObject rcvr, final double value
public final double doubleValueSetOrUnset(final SMutableObject rcvr, final double value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getDoubleAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
accessor.markPrimAsSet(rcvr, primMarkProfile);
@@ -193,7 +193,7 @@ public final double doubleValueSetOrUnset(final SMutableObject rcvr, final doubl
public final double doubleValueSet(final SImmutableObject rcvr, final double value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getDoubleAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -208,7 +208,7 @@ public final double doubleValueSet(final SImmutableObject rcvr, final double val
public final double doubleValueSetOrUnset(final SImmutableObject rcvr, final double value,
@Cached("createProfile()") final IntValueProfile primMarkProfile,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getDoubleAccessor(cachedLayout)") final AbstractPrimitiveAccessor accessor) {
accessor.write(rcvr, value);
accessor.markPrimAsSet(rcvr, primMarkProfile);
@@ -222,7 +222,7 @@ public final double doubleValueSetOrUnset(final SImmutableObject rcvr, final dou
limit = "LIMIT")
public final Object objectValue(final SImmutableObject rcvr, final Object value,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getObjectAccessor(cachedLayout)") final AbstractObjectAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -235,7 +235,7 @@ public final Object objectValue(final SImmutableObject rcvr, final Object value,
limit = "LIMIT")
public final Object objectValue(final SMutableObject rcvr, final Object value,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getObjectAccessor(cachedLayout)") final AbstractObjectAccessor accessor) {
accessor.write(rcvr, value);
return value;
@@ -248,7 +248,7 @@ public final Object objectValue(final SMutableObject rcvr, final Object value,
limit = "LIMIT")
public final Object unwritten(final SObject rcvr, final Object value,
@Cached("rcvr.getObjectLayout()") final ObjectLayout cachedLayout,
- @Cached("cachedLayout.getAssumption()") final Assumption isLatestLayout,
+ @Cached("cachedLayout.getIsLatestAssumption()") final Assumption isLatestLayout,
@Cached("getUnwritten(cachedLayout)") final StorageLocation location) {
CompilerAsserts.neverPartOfCompilation("should never be part of a compiled AST.");
ObjectTransitionSafepoint.INSTANCE.writeUninitializedSlot(rcvr, slot, value);
diff --git a/src/som/interpreter/objectstorage/ObjectLayout.java b/src/som/interpreter/objectstorage/ObjectLayout.java
index ac3984d89..2494ae9ea 100644
--- a/src/som/interpreter/objectstorage/ObjectLayout.java
+++ b/src/som/interpreter/objectstorage/ObjectLayout.java
@@ -95,7 +95,7 @@ public void checkIsLatest() throws InvalidAssumptionException {
latestLayoutForClass.check();
}
- Assumption getAssumption() {
+ public Assumption getIsLatestAssumption() {
return latestLayoutForClass;
}
diff --git a/src/som/primitives/ObjectPrims.java b/src/som/primitives/ObjectPrims.java
index 6caf600ac..625087962 100644
--- a/src/som/primitives/ObjectPrims.java
+++ b/src/som/primitives/ObjectPrims.java
@@ -23,14 +23,17 @@
import som.interpreter.nodes.nary.UnaryBasicOperation;
import som.interpreter.nodes.nary.UnaryExpressionNode;
import som.primitives.ObjectPrimsFactory.IsValueFactory;
+import som.vm.constants.Classes;
import som.vm.constants.Nil;
import som.vmobjects.SAbstractObject;
+import som.vmobjects.SArray;
import som.vmobjects.SArray.SImmutableArray;
import som.vmobjects.SArray.SMutableArray;
import som.vmobjects.SBlock;
import som.vmobjects.SClass;
import som.vmobjects.SObject.SImmutableObject;
import som.vmobjects.SObject.SMutableObject;
+import som.vmobjects.SObjectWithClass;
import som.vmobjects.SObjectWithClass.SObjectWithoutFields;
import som.vmobjects.SSymbol;
import tools.dym.Tags.OpComparison;
@@ -70,8 +73,38 @@ protected boolean hasTagIgnoringEagerness(final Class extends Tag> tag) {
@GenerateNodeFactory
@Primitive(primitive = "objClass:")
public abstract static class ClassPrim extends UnaryExpressionNode {
+
+ public abstract SClass executeEvaluated(Object receiver);
+
+ @Specialization
+ public final SClass doArray(final SArray rcvr) {
+ return rcvr.getSOMClass();
+ }
+
+ @Specialization
+ public final SClass doBlock(final SBlock rcvr) {
+ return Classes.blockClass;
+ }
+
+ @Specialization
+ public final SClass doSymbol(final SSymbol rcvr) {
+ return Classes.symbolClass;
+ }
+
+ @Specialization
+ public final SClass doObjectWithClass(final SObjectWithClass rcvr) {
+ return rcvr.getSOMClass();
+ }
+
+ @Specialization
+ public final SClass doFarRef(final SFarReference rcvr) {
+ return rcvr.getSOMClass();
+ }
+
@Specialization
public final SClass doSAbstractObject(final SAbstractObject receiver) {
+ VM.thisMethodNeedsToBeOptimized("Should specialize this if performance critical");
+ Output.println(receiver.getSOMClass().toString());
return receiver.getSOMClass();
}
diff --git a/src/som/primitives/SystemPrims.java b/src/som/primitives/SystemPrims.java
index 3f917372e..312739607 100644
--- a/src/som/primitives/SystemPrims.java
+++ b/src/som/primitives/SystemPrims.java
@@ -70,7 +70,7 @@
public final class SystemPrims {
/** File extension for SOMns extensions with Java code. */
- private static final String EXTENSION_EXT = ".jar";
+ public static final String EXTENSION_EXT = ".jar";
@CompilationFinal public static SObjectWithClass SystemModule;
@@ -311,7 +311,7 @@ public abstract static class TimePrim extends UnaryBasicOperation {
@Specialization
public final long doSObject(final Object receiver) {
- if (VmSettings.REPLAY) {
+ if (VmSettings.REPLAY && TraceParser.hasExternalData()) {
return TraceParser.getLongSysCallResult();
}
@@ -353,7 +353,7 @@ public final Object doSObject(final Object receiver) {
SnapshotBuffer sb = new SnapshotBuffer(atp);
ta.replaceSnapshotRecord();
- if (!sb.getRecord().containsObject(receiver)) {
+ if (!sb.getRecord().containsObjectUnsync(receiver)) {
SClass clazz = Types.getClassOf(receiver);
clazz.serialize(receiver, sb);
DeserializationBuffer bb = sb.getBuffer();
@@ -392,7 +392,7 @@ public abstract static class TicksPrim extends UnaryBasicOperation implements Op
@Specialization
public final long doSObject(final Object receiver) {
- if (VmSettings.REPLAY) {
+ if (VmSettings.REPLAY && TraceParser.hasExternalData()) {
return TraceParser.getLongSysCallResult();
}
diff --git a/src/som/primitives/TimerPrim.java b/src/som/primitives/TimerPrim.java
index 752a9698f..83916be69 100644
--- a/src/som/primitives/TimerPrim.java
+++ b/src/som/primitives/TimerPrim.java
@@ -7,7 +7,6 @@
import java.util.TimerTask;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiConsumer;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -31,6 +30,7 @@
import som.vmobjects.SSymbol;
import tools.concurrency.TraceParser;
import tools.concurrency.TracingActors.ReplayActor;
+import tools.replay.ExternalDataSource;
import tools.replay.actors.ActorExecutionTrace;
import tools.replay.actors.ExternalEventualMessage.ExternalDirectMessage;
import tools.replay.nodes.TraceActorContextNode;
@@ -121,9 +121,9 @@ private void performOnReplay(final Object target, final Actor targetActor) {
}
}
- private static final class TimeDataSource implements BiConsumer {
+ private static final class TimeDataSource implements ExternalDataSource {
@Override
- public void accept(final Short method, final Integer dataId) {
+ public void requestExternalMessage(final short method, final int dataId) {
assert VmSettings.REPLAY;
synchronized (REPLAY_LOCK) {
SFarReference target = replayTargetMap.remove(dataId);
diff --git a/src/som/primitives/actors/PromisePrims.java b/src/som/primitives/actors/PromisePrims.java
index d64b91782..e8ec11375 100644
--- a/src/som/primitives/actors/PromisePrims.java
+++ b/src/som/primitives/actors/PromisePrims.java
@@ -120,7 +120,7 @@ protected boolean hasTagIgnoringEagerness(final Class extends Tag> tag) {
@TruffleBoundary
public static RootCallTarget createReceived(final SBlock callback) {
RootCallTarget target = callback.getMethod().getCallTarget();
- ReceivedCallback node = new ReceivedCallback(target);
+ ReceivedCallback node = new ReceivedCallback(target, callback.getMethod().getSignature());
return Truffle.getRuntime().createCallTarget(node);
}
diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java
index ab7c59be9..f99fdb0e4 100644
--- a/src/som/vm/ObjectSystem.java
+++ b/src/som/vm/ObjectSystem.java
@@ -45,14 +45,16 @@
import som.vmobjects.SObjectWithClass.SObjectWithoutFields;
import som.vmobjects.SSymbol;
import tools.concurrency.TracingActors;
+import tools.concurrency.TracingActors.ReplayActor;
import tools.language.StructuralProbe;
+import tools.snapshot.SnapshotBackend;
+import tools.snapshot.deserialization.SnapshotParser;
import tools.snapshot.nodes.AbstractArraySerializationNodeGen.ArraySerializationNodeFactory;
import tools.snapshot.nodes.AbstractArraySerializationNodeGen.TransferArraySerializationNodeFactory;
import tools.snapshot.nodes.AbstractArraySerializationNodeGen.ValueArraySerializationNodeFactory;
import tools.snapshot.nodes.AbstractSerializationNode;
import tools.snapshot.nodes.BlockSerializationNodeFactory;
-import tools.snapshot.nodes.MessageSerializationNodeFactory;
-import tools.snapshot.nodes.ObjectSerializationNodesFactory.SObjectWithoutFieldsSerializationNodeFactory;
+import tools.snapshot.nodes.BlockSerializationNodeFactory.FrameSerializationNodeFactory;
import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.BooleanSerializationNodeFactory;
import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.ClassSerializationNodeFactory;
import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.DoubleSerializationNodeFactory;
@@ -100,6 +102,9 @@ public ObjectSystem(final SourcecodeCompiler compiler,
this.compiler = compiler;
structuralProbe = probe;
loadedModules = EconomicMap.create();
+ if (VmSettings.SNAPSHOT_REPLAY) {
+ SnapshotBackend.registerLoadedModules(loadedModules);
+ }
this.vm = vm;
}
@@ -289,54 +294,63 @@ public SObjectWithoutFields initialize() {
if (VmSettings.SNAPSHOTS_ENABLED) {
SerializerRootNode.initializeSerialization(compiler.getLanguage());
- topDef.initializeClass(Classes.topClass, null,
- SObjectWithoutFieldsSerializationNodeFactory.getInstance()); // Top doesn't have a
- // super class
- thingDef.initializeClass(Classes.thingClass, Classes.topClass,
- SObjectWithoutFieldsSerializationNodeFactory.getInstance());
- valueDef.initializeClass(Classes.valueClass, Classes.thingClass, true, false, false,
- SObjectWithoutFieldsSerializationNodeFactory.getInstance());
- objectDef.initializeClass(Classes.objectClass, Classes.thingClass,
- SObjectWithoutFieldsSerializationNodeFactory.getInstance());
+ topDef.initializeClass(Classes.topClass, null); // Top doesn't have a
+ // super class
+ thingDef.initializeClass(Classes.thingClass, Classes.topClass);
+ valueDef.initializeClass(Classes.valueClass, Classes.thingClass, true, false, false);
+ objectDef.initializeClass(Classes.objectClass, Classes.thingClass);
classDef.initializeClass(Classes.classClass, Classes.objectClass,
- ClassSerializationNodeFactory.getInstance());
+ ClassSerializationNodeFactory.getInstance(),
+ ClassSerializationNodeFactory.create());
transferDef.initializeClass(Classes.transferClass, Classes.objectClass, false, true,
- false, SObjectWithoutFieldsSerializationNodeFactory.getInstance());
+ false);
metaclassDef.initializeClass(Classes.metaclassClass, Classes.classClass,
- ClassSerializationNodeFactory.getInstance());
+ ClassSerializationNodeFactory.getInstance(),
+ ClassSerializationNodeFactory.create());
nilDef.initializeClass(Classes.nilClass, Classes.valueClass,
- NilSerializationNodeFactory.getInstance());
+ NilSerializationNodeFactory.getInstance(),
+ NilSerializationNodeFactory.create());
- arrayReadMixinDef.initializeClass(Classes.arrayReadMixinClass, Classes.objectClass,
- SObjectWithoutFieldsSerializationNodeFactory.getInstance());
+ arrayReadMixinDef.initializeClass(Classes.arrayReadMixinClass, Classes.objectClass);
arrayDef.initializeClass(Classes.arrayClass,
new SClass[] {Classes.objectClass, Classes.arrayReadMixinClass}, false, false, true,
- ArraySerializationNodeFactory.getInstance());
+ ArraySerializationNodeFactory.getInstance(),
+ ArraySerializationNodeFactory.create());
valueArrayDef.initializeClass(Classes.valueArrayClass,
new SClass[] {Classes.valueClass, Classes.arrayReadMixinClass}, false, false, true,
- ValueArraySerializationNodeFactory.getInstance());
+ ValueArraySerializationNodeFactory.getInstance(),
+ ValueArraySerializationNodeFactory.create());
transferArrayDef.initializeClass(Classes.transferArrayClass,
new SClass[] {Classes.arrayClass, Classes.transferClass}, false, false, true,
- TransferArraySerializationNodeFactory.getInstance());
+ TransferArraySerializationNodeFactory.getInstance(),
+ TransferArraySerializationNodeFactory.create());
integerDef.initializeClass(Classes.integerClass, Classes.valueClass,
- IntegerSerializationNodeFactory.getInstance());
+ IntegerSerializationNodeFactory.getInstance(),
+ IntegerSerializationNodeFactory.create());
stringDef.initializeClass(Classes.stringClass, Classes.valueClass,
- StringSerializationNodeFactory.getInstance());
+ StringSerializationNodeFactory.getInstance(),
+ StringSerializationNodeFactory.create());
doubleDef.initializeClass(Classes.doubleClass, Classes.valueClass,
- DoubleSerializationNodeFactory.getInstance());
+ DoubleSerializationNodeFactory.getInstance(),
+ DoubleSerializationNodeFactory.create());
symbolDef.initializeClass(Classes.symbolClass, Classes.stringClass,
- SymbolSerializationNodeFactory.getInstance());
+ SymbolSerializationNodeFactory.getInstance(),
+ SymbolSerializationNodeFactory.create());
booleanDef.initializeClass(Classes.booleanClass, Classes.valueClass,
- BooleanSerializationNodeFactory.getInstance());
+ BooleanSerializationNodeFactory.getInstance(),
+ BooleanSerializationNodeFactory.create());
trueDef.initializeClass(Classes.trueClass, Classes.booleanClass,
- TrueSerializationNodeFactory.getInstance());
+ TrueSerializationNodeFactory.getInstance(),
+ TrueSerializationNodeFactory.create());
falseDef.initializeClass(Classes.falseClass, Classes.booleanClass,
- FalseSerializationNodeFactory.getInstance());
+ FalseSerializationNodeFactory.getInstance(),
+ FalseSerializationNodeFactory.create());
blockDef.initializeClass(Classes.blockClass, Classes.objectClass,
- BlockSerializationNodeFactory.getInstance());
+ BlockSerializationNodeFactory.getInstance(),
+ BlockSerializationNodeFactory.create());
} else {
topDef.initializeClass(Classes.topClass, null); // Top doesn't have a
// super class
@@ -410,11 +424,16 @@ public SObjectWithoutFields initialize() {
Classes.blockClass.getSOMClass()
.setClassGroup(Classes.metaclassClass.getInstanceFactory());
- // these classes are not exposed in Newspeak directly, and thus, do not yet have a class
- // factory
- setDummyClassFactory(Classes.messageClass, MessageSerializationNodeFactory.getInstance());
- setDummyClassFactory(Classes.methodClass,
- SInvokableSerializationNodeFactory.getInstance());
+ if (VmSettings.SNAPSHOTS_ENABLED) {
+ // these classes are not exposed in Newspeak directly, and thus, do not yet have a class
+ // factory
+ setDummyClassFactory(Classes.messageClass, null, null); // MessageSerializationNodeFactory.getInstance());
+ setDummyClassFactory(Classes.frameClass, FrameSerializationNodeFactory.getInstance(),
+ null);
+ setDummyClassFactory(Classes.methodClass,
+ SInvokableSerializationNodeFactory.getInstance(),
+ SInvokableSerializationNodeFactory.create());
+ }
SClass kernelClass = kernelModule.instantiateClass(Nil.nilObject, Classes.objectClass);
KernelObj.kernel.setClass(kernelClass);
@@ -460,13 +479,15 @@ public SObjectWithoutFields initialize() {
}
public void setDummyClassFactory(final SClass clazz,
- final NodeFactory extends AbstractSerializationNode> serializerFactory) {
+ final NodeFactory extends AbstractSerializationNode> serializerFactory,
+ final AbstractSerializationNode deserializer) {
if (VmSettings.SNAPSHOTS_ENABLED) {
ClassFactory classFactory = new ClassFactory(clazz.getSOMClass().getName(), null,
null, null, true,
true, false,
null, false,
- null, serializerFactory);
+ null);
+ classFactory.customizeSerialization(serializerFactory, deserializer);
clazz.setClassGroup(classFactory);
clazz.initializeStructure(null, null, null, true, false, false, classFactory);
@@ -481,6 +502,10 @@ private static void setSlot(final SObject obj, final String slotName,
}
private int handlePromiseResult(final SPromise promise) {
+ if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) {
+ SnapshotBackend.registerResultPromise(promise);
+ }
+
// This is an attempt to prevent to get stuck indeterminately.
// We check whether there is activity on any of the pools.
// And, we exit when either the main promise is resolved, or an exit was requested.
@@ -529,6 +554,10 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m
Object platform = platformModule.instantiateObject(platformClass, vmMirror);
ObjectTransitionSafepoint.INSTANCE.unregister();
+ if (VmSettings.SNAPSHOTS_ENABLED) {
+ SnapshotBackend.initialize(vm);
+ }
+
SSymbol start = Symbols.symbolFor("start");
SourceSection source;
@@ -566,6 +595,26 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m
}
}
+ @TruffleBoundary
+ public int executeApplicationFromSnapshot(final SObjectWithoutFields vmMirror) {
+ mainThreadCompleted = new CompletableFuture<>();
+ Output.println("Parsing Snapshot...");
+ ObjectTransitionSafepoint.INSTANCE.register();
+ Object platform = platformModule.instantiateObject(platformClass, vmMirror);
+
+ SnapshotBackend.initialize(vm);
+ SnapshotParser.inflate(vm);
+
+ ObjectTransitionSafepoint.INSTANCE.unregister();
+
+ Output.println(
+ "Finished restoring snapshot with " + SnapshotParser.getObjectCnt() + " Objects");
+ ReplayActor.scheduleAllActors(vm.getActorPool());
+
+ SPromise result = SnapshotParser.getResultPromise();
+ return handlePromiseResult(result);
+ }
+
@TruffleBoundary
public Object execute(final String selector) {
SInvokable method = (SInvokable) platformClass.getSOMClass().lookupMessage(
diff --git a/src/som/vm/Symbols.java b/src/som/vm/Symbols.java
index 619183a76..25a1d3b72 100644
--- a/src/som/vm/Symbols.java
+++ b/src/som/vm/Symbols.java
@@ -1,5 +1,7 @@
package som.vm;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -24,6 +26,19 @@ public static SSymbol symbolFor(final String string) {
return result;
}
+ public static void addSymbolFor(final String string, final short id) {
+ String interned = string.intern();
+ SSymbol result = symbolTable.get(interned);
+
+ // have to do this due to the statically created symbols further down.
+ if (result != null) {
+ return;
+ }
+
+ result = new SSymbol(interned, id);
+ symbolTable.put(string, result);
+ }
+
private Symbols() {}
@Override
@@ -31,6 +46,10 @@ public SSymbol getId(final String id) {
return symbolFor(id);
}
+ public static Collection getSymbols() {
+ return Collections.unmodifiableCollection(symbolTable.values());
+ }
+
private static final HashMap symbolTable = new HashMap<>();
public static final SSymbol NEW = symbolFor("new");
diff --git a/src/som/vm/VmSettings.java b/src/som/vm/VmSettings.java
index e5fdd9ce8..0a6eab22b 100644
--- a/src/som/vm/VmSettings.java
+++ b/src/som/vm/VmSettings.java
@@ -19,6 +19,7 @@ public class VmSettings implements Settings {
public static final boolean REPLAY;
public static final boolean KOMPOS_TRACING;
public static final boolean TRACE_SMALL_IDS;
+ public static final boolean USE_TRACING_ACTORS;
public static final boolean SNAPSHOTS_ENABLED;
public static final boolean TRACK_SNAPSHOT_ENTITIES;
public static final boolean TEST_SNAPSHOTS;
@@ -39,6 +40,9 @@ public class VmSettings implements Settings {
public static final String BASE_DIRECTORY;
+ public static final boolean SNAPSHOT_REPLAY;
+ public static final int SNAPSHOT_INLINING_DEPTH;
+
static {
String prop = System.getProperty("som.threads");
if (prop == null) {
@@ -65,6 +69,7 @@ public class VmSettings implements Settings {
TEST_SERIALIZE_ALL = getBool("som.actorSnapshotAll", false);
SNAPSHOTS_ENABLED = getBool("som.actorSnapshot", false) || TEST_SNAPSHOTS;
TRACK_SNAPSHOT_ENTITIES = (REPLAY && SNAPSHOTS_ENABLED) || TEST_SNAPSHOTS;
+ SNAPSHOT_REPLAY = REPLAY && SNAPSHOTS_ENABLED;
boolean dm = getBool("som.dynamicMetrics", false);
DYNAMIC_METRICS = dm;
@@ -74,11 +79,15 @@ public class VmSettings implements Settings {
IGV_DUMP_AFTER_PARSING = getBool("som.igvDumpAfterParsing", false);
ANSI_COLOR_IN_OUTPUT = getBool("som.useAnsiColoring", false);
+ USE_TRACING_ACTORS = ACTOR_TRACING || SNAPSHOTS_ENABLED;
+
BUFFER_SIZE = getInteger("som.buffSize", 1024 * 1024);
BUFFERS_PER_THREAD = getInteger("som.buffPerThread", 4);
BUFFER_TIMEOUT = getInteger("som.buffDelay", 50);
RECYCLE_BUFFERS = getBool("som.bufferRecycling", true);
+ SNAPSHOT_INLINING_DEPTH = getInteger("som.snapshotInliningDepth", 8);
+
BASE_DIRECTORY = System.getProperty("som.baseDir", System.getProperty("user.dir"));
}
diff --git a/src/som/vm/constants/Classes.java b/src/som/vm/constants/Classes.java
index e8ef6c3bb..4fc1ddd4f 100644
--- a/src/som/vm/constants/Classes.java
+++ b/src/som/vm/constants/Classes.java
@@ -32,6 +32,7 @@ public final class Classes {
// dummy class for message deserialization
public static final SClass messageClass;
+ public static final SClass frameClass;
// These classes can be statically preinitialized.
static {
@@ -65,5 +66,6 @@ public final class Classes {
blockClass = ObjectSystem.newEmptyClassWithItsClass("Block");
messageClass = ObjectSystem.newEmptyClassWithItsClass("Message");
+ frameClass = ObjectSystem.newEmptyClassWithItsClass("BlockFrame");
}
}
diff --git a/src/som/vmobjects/SClass.java b/src/som/vmobjects/SClass.java
index b5e133502..d27c23875 100644
--- a/src/som/vmobjects/SClass.java
+++ b/src/som/vmobjects/SClass.java
@@ -31,6 +31,7 @@
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
@@ -40,14 +41,18 @@
import som.compiler.MixinDefinition;
import som.compiler.MixinDefinition.ClassSlotDefinition;
import som.compiler.MixinDefinition.SlotDefinition;
+import som.interpreter.actors.Actor.ActorProcessingThread;
import som.interpreter.nodes.dispatch.Dispatchable;
import som.interpreter.objectstorage.ClassFactory;
import som.interpreter.objectstorage.ObjectLayout;
import som.vm.VmSettings;
import som.vm.constants.Classes;
+import tools.concurrency.TracingActors.TracingActor;
import tools.snapshot.SnapshotBackend;
import tools.snapshot.SnapshotBuffer;
+import tools.snapshot.deserialization.DeserializationBuffer;
import tools.snapshot.nodes.AbstractSerializationNode;
+import tools.snapshot.nodes.MessageSerializationNode;
// TODO: should we move more of that out of SClass and use the corresponding
@@ -69,6 +74,9 @@ public final class SClass extends SObjectWithClass {
@CompilationFinal private boolean isArray; // is a subclass of Array
@CompilationFinal private ClassFactory instanceClassGroup; // the factory for this object
+ @CompilationFinal private int identity;
+
+ @CompilationFinal private TracingActor ownerOfOuter;
protected final SObjectWithClass enclosingObject;
private final MaterializedFrame context;
@@ -191,17 +199,39 @@ public void initializeStructure(final MixinDefinition mixinDef,
this.isTransferObject = isTransferObject;
this.isArray = isArray;
this.instanceClassGroup = classFactory;
+
+ if (VmSettings.SNAPSHOTS_ENABLED) {
+ identity = instanceClassGroup.createIdentity();
+
+ if (!VmSettings.REPLAY && !VmSettings.TEST_SNAPSHOTS && enclosingObject != null) {
+
+ if (Thread.currentThread() instanceof ActorProcessingThread) {
+ this.ownerOfOuter =
+ (TracingActor) ((ActorProcessingThread) Thread.currentThread()).getCurrentActor();
+ }
+ }
+ }
// assert instanceClassGroup != null || !ObjectSystem.isInitialized();
if (VmSettings.TRACK_SNAPSHOT_ENTITIES) {
- if (mixinDef != null) {
- SnapshotBackend.registerClass(mixinDef.getIdentifier(), this);
- } else {
- SnapshotBackend.registerClass(classFactory.getClassName(), this);
- }
+ SnapshotBackend.registerClass(this);
}
}
+ public TracingActor getOwnerOfOuter() {
+ return ownerOfOuter;
+ }
+
+ public void customizeSerializerFactory(
+ final NodeFactory extends AbstractSerializationNode> factory,
+ final AbstractSerializationNode deserializer) {
+ instanceClassGroup.customizeSerialization(factory, deserializer);
+ }
+
+ public NodeFactory extends AbstractSerializationNode> getSerializerFactory() {
+ return instanceClassGroup.getSerializerFactory();
+ }
+
/**
* This method checks whether a given class was created from `this`
* class. The check is made by comparing unique identifiers, the
@@ -356,12 +386,21 @@ public MaterializedFrame getContext() {
public void serialize(final Object o, final SnapshotBuffer sb) {
assert instanceClassGroup != null;
- if (!sb.getRecord().containsObject(o)) {
- getSerializer().execute(o, sb);
+ if (!sb.getRecord().containsObjectUnsync(o)) {
+ instanceClassGroup.serialize(o, sb);
}
}
- public AbstractSerializationNode getSerializer() {
- return instanceClassGroup.getSerializer();
+ public Object deserialize(final DeserializationBuffer bb) {
+ if (this == Classes.messageClass) {
+ return MessageSerializationNode.deserializeMessage(bb);
+ }
+
+ return this.instanceClassGroup.deserialize(bb, this);
+ }
+
+ public int getIdentity() {
+ assert identity != 0;
+ return identity;
}
}
diff --git a/src/som/vmobjects/SInvokable.java b/src/som/vmobjects/SInvokable.java
index 869b723c4..a788fc7fe 100644
--- a/src/som/vmobjects/SInvokable.java
+++ b/src/som/vmobjects/SInvokable.java
@@ -27,19 +27,23 @@
import static som.interpreter.TruffleCompiler.transferToInterpreterAndInvalidate;
+import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.source.SourceSection;
import som.compiler.AccessModifier;
import som.compiler.MixinDefinition;
import som.interpreter.Invokable;
+import som.interpreter.Method;
import som.interpreter.nodes.dispatch.AbstractDispatchNode;
import som.interpreter.nodes.dispatch.CachedDispatchNode;
import som.interpreter.nodes.dispatch.DispatchGuard;
@@ -48,6 +52,8 @@
import som.vm.Symbols;
import som.vm.VmSettings;
import som.vm.constants.Classes;
+import tools.snapshot.nodes.BlockSerializationNode.FrameSerializationNode;
+import tools.snapshot.nodes.BlockSerializationNodeFactory.FrameSerializationNodeFactory;
public class SInvokable extends SAbstractObject implements Dispatchable {
@@ -58,8 +64,9 @@ public class SInvokable extends SAbstractObject implements Dispatchable {
private final SSymbol signature;
private final SInvokable[] embeddedBlocks;
- @CompilationFinal private MixinDefinition holder;
- @CompilationFinal private RootCallTarget atomicCallTarget;
+ @CompilationFinal private FrameSerializationNode frameSerializer;
+ @CompilationFinal private MixinDefinition holder;
+ @CompilationFinal private RootCallTarget atomicCallTarget;
public SInvokable(final SSymbol signature,
final AccessModifier accessModifier,
@@ -182,6 +189,16 @@ public final SourceSection getSourceSection() {
return invokable.getSourceSection();
}
+ public FrameSerializationNode getFrameSerializer() {
+ if (frameSerializer == null) {
+ CompilerDirectives.transferToInterpreter();
+ FrameDescriptor fd = ((Method) invokable).getLexicalScope().getOuterMethod()
+ .getMethod().getFrameDescriptor();
+ frameSerializer = FrameSerializationNodeFactory.create(fd);
+ }
+ return frameSerializer;
+ }
+
@Override
public final AbstractDispatchNode getDispatchNode(final Object rcvr,
final Object firstArg, final AbstractDispatchNode next, final boolean forAtomic) {
@@ -203,13 +220,18 @@ public final String typeForErrors() {
return "method";
}
+ @TruffleBoundary
+ private URI getURI(final SourceSection source) {
+ return source.getSource().getURI();
+ }
+
public SSymbol getIdentifier() {
if (holder != null) {
return Symbols.symbolFor(
holder.getIdentifier().getString() + "." + this.signature.getString());
} else if (invokable.getSourceSection() != null) {
// TODO find a better solution than charIndex
- Path absolute = Paths.get(invokable.getSourceSection().getSource().getURI());
+ Path absolute = Paths.get(getURI(invokable.getSourceSection()));
Path relative =
Paths.get(VmSettings.BASE_DIRECTORY).toAbsolutePath().relativize(absolute);
return Symbols.symbolFor(relative.toString() + ":"
diff --git a/src/som/vmobjects/SSymbol.java b/src/som/vmobjects/SSymbol.java
index cd51e9815..93b03782a 100644
--- a/src/som/vmobjects/SSymbol.java
+++ b/src/som/vmobjects/SSymbol.java
@@ -41,8 +41,7 @@ public final class SSymbol extends SAbstractObject {
public SSymbol(final String value) {
string = value;
numberOfSignatureArguments = determineNumberOfSignatureArguments();
- if (VmSettings.KOMPOS_TRACING || VmSettings.ACTOR_TRACING
- || VmSettings.TRACK_SNAPSHOT_ENTITIES) {
+ if (VmSettings.KOMPOS_TRACING || VmSettings.SNAPSHOTS_ENABLED) {
symbolId = (short) idGenerator.getAndIncrement();
TracingBackend.logSymbol(this);
if (VmSettings.TRACK_SNAPSHOT_ENTITIES) {
@@ -53,6 +52,24 @@ public SSymbol(final String value) {
}
}
+ /**
+ * Used for snapshot-based replay.
+ */
+ public SSymbol(final String value, final short id) {
+ string = value;
+ numberOfSignatureArguments = determineNumberOfSignatureArguments();
+ symbolId = id;
+
+ // avoid multiple symbols with same id
+ if (id >= idGenerator.get()) {
+ idGenerator.set(id + 1);
+ }
+
+ if (VmSettings.TRACK_SNAPSHOT_ENTITIES) {
+ SnapshotBackend.registerSymbol(this);
+ }
+ }
+
@Override
public SClass getSOMClass() {
assert Classes.symbolClass != null;
diff --git a/src/tools/concurrency/TraceParser.java b/src/tools/concurrency/TraceParser.java
index c6eb5e0a5..8b53c5826 100644
--- a/src/tools/concurrency/TraceParser.java
+++ b/src/tools/concurrency/TraceParser.java
@@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Queue;
import som.Output;
@@ -42,10 +43,16 @@ private enum TraceRecord {
private static TraceParser parser;
private static String traceName =
- VmSettings.TRACE_FILE + (VmSettings.SNAPSHOTS_ENABLED ? ".0" : "");
+ VmSettings.TRACE_FILE + (VmSettings.SNAPSHOTS_ENABLED ? ".1" : "");
private final TraceRecord[] parseTable;
+ public static boolean hasExternalData() {
+ ReplayActor ra = (ReplayActor) EventualMessage.getActorCurrentMessageIsExecutionOn();
+ long key = (((long) ra.getActorId()) << 32) | ra.peekDataId();
+ return parser.externalDataDict.containsKey(key);
+ }
+
public static ByteBuffer getExternalData(final int actorId, final int dataId) {
long key = (((long) actorId) << 32) | dataId;
long pos = parser.externalDataDict.get(key);
@@ -82,7 +89,12 @@ public static synchronized Queue getExpectedMessages(final int re
parser.parseTrace();
}
- return parser.actors.get(replayId).getExpectedMessages();
+ ActorNode an = parser.actors.get(replayId);
+ if (an != null) {
+ return an.getExpectedMessages();
+ } else {
+ return new LinkedList();
+ }
}
public static synchronized int getReplayId(final int parentId, final int childNo) {
diff --git a/src/tools/concurrency/TracingActivityThread.java b/src/tools/concurrency/TracingActivityThread.java
index 2eb0e5718..6782a82ad 100644
--- a/src/tools/concurrency/TracingActivityThread.java
+++ b/src/tools/concurrency/TracingActivityThread.java
@@ -9,9 +9,11 @@
import som.vm.Activity;
import som.vm.VmSettings;
import tools.TraceData;
+import tools.concurrency.TracingActors.TracingActor;
import tools.debugger.SteppingStrategy;
import tools.debugger.entities.EntityType;
import tools.debugger.entities.SteppingType;
+import tools.replay.nodes.TraceActorContextNode;
import tools.snapshot.SnapshotBackend;
import tools.snapshot.SnapshotBuffer;
@@ -31,6 +33,7 @@ public abstract class TracingActivityThread extends ForkJoinWorkerThread {
protected final TraceBuffer traceBuffer;
protected SnapshotBuffer snapshotBuffer;
+ protected ArrayList messageLocations;
protected Object[] externalData;
protected int extIndex = 0;
@@ -85,6 +88,7 @@ public TracingActivityThread(final ForkJoinPool pool) {
super(pool);
if (VmSettings.SNAPSHOTS_ENABLED) {
this.snapshotBuffer = new SnapshotBuffer((ActorProcessingThread) this);
+ this.messageLocations = new ArrayList<>();
}
if (VmSettings.ACTOR_TRACING || VmSettings.KOMPOS_TRACING) {
@@ -92,6 +96,11 @@ public TracingActivityThread(final ForkJoinPool pool) {
traceBuffer = TraceBuffer.create(threadId);
nextEntityId = 1 + (threadId << TraceData.ENTITY_ID_BITS);
externalData = new Object[EXTERNAL_BUFFER_SIZE];
+ } else if (VmSettings.SNAPSHOTS_ENABLED) {
+ threadId = threadIdGen.getAndIncrement();
+ nextEntityId = 0;
+ traceBuffer = null;
+ externalData = null;
} else {
threadId = 0;
nextEntityId = 0;
@@ -165,6 +174,11 @@ public final void addExternalData(final Object data) {
}
}
+ public final void addMessageLocation(final long actorId, final long messageAdress) {
+ messageLocations.add(actorId);
+ messageLocations.add(messageAdress);
+ }
+
@Override
protected void onStart() {
super.onStart();
@@ -181,7 +195,7 @@ protected void onTermination(final Throwable exception) {
TracingBackend.unregisterThread(this);
}
if (VmSettings.SNAPSHOTS_ENABLED) {
- SnapshotBackend.registerSnapshotBuffer(snapshotBuffer);
+ SnapshotBackend.registerSnapshotBuffer(snapshotBuffer, messageLocations);
}
super.onTermination(exception);
}
@@ -215,16 +229,26 @@ public byte getSnapshotId() {
}
private void newSnapshot() {
- traceBuffer.swapStorage();
- if (extIndex != 0) {
- TracingBackend.addExternalData(externalData, this);
- externalData = new Object[EXTERNAL_BUFFER_SIZE];
- extIndex = 0;
+ TracingActor ta = (TracingActor) ((ActorProcessingThread) this).getCurrentActor();
+
+ if (VmSettings.ACTOR_TRACING) {
+ TraceActorContextNode tracer = ta.getActorContextNode();
+ traceBuffer.swapStorage();
+ if (tracer != null) {
+ tracer.trace(ta);
+ }
+
+ if (extIndex != 0) {
+ TracingBackend.addExternalData(externalData, this);
+ externalData = new Object[EXTERNAL_BUFFER_SIZE];
+ extIndex = 0;
+ }
}
this.snapshotId = SnapshotBackend.getSnapshotVersion();
// get net snapshotbuffer
this.snapshotBuffer = new SnapshotBuffer((ActorProcessingThread) this);
+ this.messageLocations = new ArrayList<>();
}
}
diff --git a/src/tools/concurrency/TracingActors.java b/src/tools/concurrency/TracingActors.java
index 05920acef..ff3e54108 100644
--- a/src/tools/concurrency/TracingActors.java
+++ b/src/tools/concurrency/TracingActors.java
@@ -1,15 +1,15 @@
package tools.concurrency;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
-import java.util.WeakHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiConsumer;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import som.Output;
@@ -24,7 +24,9 @@
import tools.concurrency.TraceParser.MessageRecord;
import tools.concurrency.TraceParser.PromiseMessageRecord;
import tools.debugger.WebDebugger;
+import tools.replay.ExternalDataSource;
import tools.replay.actors.ExternalMessage;
+import tools.replay.nodes.TraceActorContextNode;
import tools.snapshot.SnapshotRecord;
import tools.snapshot.deserialization.DeserializationBuffer;
@@ -35,7 +37,8 @@ public static class TracingActor extends Actor {
protected final int actorId;
protected short ordering;
protected int nextDataID;
- protected SnapshotRecord snapshotRecord;
+
+ @CompilationFinal protected SnapshotRecord snapshotRecord;
/**
* Flag that indicates if a step-to-next-turn action has been made in the previous message.
@@ -46,7 +49,7 @@ public TracingActor(final VM vm) {
super(vm);
this.actorId = IdGen.getAndIncrement();
if (VmSettings.SNAPSHOTS_ENABLED) {
- snapshotRecord = new SnapshotRecord();
+ snapshotRecord = new SnapshotRecord(this);
}
}
@@ -55,6 +58,11 @@ protected TracingActor(final VM vm, final int id) {
this.actorId = id;
}
+ @Override
+ public String toString() {
+ return super.toString() + " #" + actorId;
+ }
+
public final int getActorId() {
return actorId;
}
@@ -67,6 +75,14 @@ public synchronized int getDataId() {
return nextDataID++;
}
+ public synchronized int peekDataId() {
+ return nextDataID;
+ }
+
+ public TraceActorContextNode getActorContextNode() {
+ return this.executor.getActorContextNode();
+ }
+
public boolean isStepToNextTurn() {
return stepToNextTurn;
}
@@ -80,7 +96,7 @@ public SnapshotRecord getSnapshotRecord() {
* For testing purposes.
*/
public void replaceSnapshotRecord() {
- this.snapshotRecord = new SnapshotRecord();
+ this.snapshotRecord = new SnapshotRecord(this);
}
@Override
@@ -118,22 +134,22 @@ public static final class ReplayActor extends TracingActor {
protected final Queue expectedMessages;
protected final ArrayList leftovers = new ArrayList<>();
private static Map actorList;
- private BiConsumer dataSource;
+ private ExternalDataSource dataSource;
private int traceBufferId;
private final long activityId;
static {
if (VmSettings.REPLAY) {
- actorList = new WeakHashMap<>();
+ actorList = new HashMap<>();
}
}
- public BiConsumer getDataSource() {
+ public ExternalDataSource getDataSource() {
assert dataSource != null;
return dataSource;
}
- public void setDataSource(final BiConsumer ds) {
+ public void setDataSource(final ExternalDataSource ds) {
if (dataSource != null) {
throw new UnsupportedOperationException("Allready has a datasource!");
}
@@ -168,7 +184,11 @@ public long getId() {
@TruffleBoundary
public ReplayActor(final VM vm) {
- super(vm, lookupId());
+ this(vm, lookupId());
+ }
+
+ public ReplayActor(final VM vm, final int id) {
+ super(vm, id);
this.activityId = TracingActivityThread.newEntityId();
@@ -215,6 +235,12 @@ public synchronized void send(final EventualMessage msg, final ForkJoinPool acto
}
}
+ public static void scheduleAllActors(final ForkJoinPool actorPool) {
+ for (ReplayActor ra : actorList.values()) {
+ ra.executeIfNecessarry(actorPool);
+ }
+ }
+
/**
* Prints a list of expected Messages and remaining mailbox content.
*
@@ -248,13 +274,14 @@ public static boolean printMissingMessages() {
for (EventualMessage em : a.leftovers) {
printMsg(em);
}
- } else if (a.firstMessage != null || a.mailboxExtension != null) {
+ } else if (a.firstMessage != null || a.mailboxExtension != null
+ || !a.leftovers.isEmpty()) {
int n = a.firstMessage != null ? 1 : 0;
n += a.mailboxExtension != null ? a.mailboxExtension.size() : 0;
Output.println(
- a.getName() + " [" + a.getId() + "] has " + n + " unexpected messages:");
+ a.getName() + " [" + a.getActorId() + "] has " + n + " unexpected messages:");
if (a.firstMessage != null) {
printMsg(a.firstMessage);
if (a.mailboxExtension != null) {
@@ -263,6 +290,11 @@ public static boolean printMissingMessages() {
}
}
}
+ if (a.leftovers != null) {
+ for (EventualMessage em : a.leftovers) {
+ printMsg(em);
+ }
+ }
}
}
return result;
@@ -338,12 +370,12 @@ private static void removeFirstExpectedMessage(final ReplayActor a) {
if (a.expectedMessages.peek() != null && a.expectedMessages.peek().isExternal()) {
if (a.expectedMessages.peek() instanceof ExternalMessageRecord) {
ExternalMessageRecord emr = (ExternalMessageRecord) a.expectedMessages.peek();
- actorList.get(emr.sender).getDataSource().accept(emr.method,
+ actorList.get(emr.sender).getDataSource().requestExternalMessage(emr.method,
emr.dataId);
} else {
ExternalPromiseMessageRecord emr =
(ExternalPromiseMessageRecord) a.expectedMessages.peek();
- actorList.get(emr.pId).getDataSource().accept(emr.method,
+ actorList.get(emr.pId).getDataSource().requestExternalMessage(emr.method,
emr.dataId);
}
}
diff --git a/src/tools/concurrency/TracingBackend.java b/src/tools/concurrency/TracingBackend.java
index 9fe60496e..3406ffa3d 100644
--- a/src/tools/concurrency/TracingBackend.java
+++ b/src/tools/concurrency/TracingBackend.java
@@ -331,6 +331,10 @@ public static void startTracingBackend() {
*/
@TruffleBoundary
public static void newSnapshot(final byte newSnapshotVersion) {
+ if (!VmSettings.ACTOR_TRACING) {
+ return;
+ }
+
TraceWorkerThread outDatedWorker;
outDatedWorker = previousWorkerThread;
@@ -581,10 +585,12 @@ public void run() {
processTraceData(null, null, null);
} else {
try (FileOutputStream traceDataStream = new FileOutputStream(f);
- FileOutputStream symbolStream = new FileOutputStream(sf);
+ FileOutputStream symbolStream =
+ VmSettings.SNAPSHOTS_ENABLED ? null : new FileOutputStream(sf);
FileOutputStream externalDataStream = new FileOutputStream(edf);
BufferedWriter symbolWriter =
- new BufferedWriter(new OutputStreamWriter(symbolStream))) {
+ VmSettings.SNAPSHOTS_ENABLED ? null
+ : new BufferedWriter(new OutputStreamWriter(symbolStream))) {
processTraceData(traceDataStream, externalDataStream, symbolWriter);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
diff --git a/src/tools/replay/actors/ActorExecutionTrace.java b/src/tools/replay/actors/ActorExecutionTrace.java
index 329c99994..f5fbf9ea8 100644
--- a/src/tools/replay/actors/ActorExecutionTrace.java
+++ b/src/tools/replay/actors/ActorExecutionTrace.java
@@ -27,8 +27,7 @@ private static TracingActivityThread getThread() {
}
public static void recordActorContext(final TracingActor actor,
- final TraceActorContextNode tracer) {
- TracingActivityThread t = getThread();
+ final TracingActivityThread t, final TraceActorContextNode tracer) {
((ActorTraceBuffer) t.getBuffer()).recordActorContext(actor, tracer);
}
diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java
index ea7fa5b1c..cfb75b920 100644
--- a/src/tools/snapshot/SnapshotBackend.java
+++ b/src/tools/snapshot/SnapshotBackend.java
@@ -1,31 +1,70 @@
package tools.snapshot;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.URI;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.graalvm.collections.EconomicMap;
+import org.graalvm.collections.MapCursor;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+import som.Output;
+import som.VM;
+import som.compiler.MixinDefinition;
+import som.interpreter.Types;
import som.interpreter.actors.Actor;
import som.interpreter.actors.EventualMessage;
+import som.interpreter.actors.SPromise;
+import som.interpreter.actors.SPromise.SResolver;
+import som.interpreter.nodes.InstantiationNode.ClassInstantiationNode;
+import som.interpreter.objectstorage.ClassFactory;
+import som.primitives.ObjectPrims.ClassPrim;
+import som.primitives.ObjectPrimsFactory.ClassPrimFactory;
+import som.vm.Symbols;
import som.vm.VmSettings;
import som.vmobjects.SClass;
import som.vmobjects.SInvokable;
+import som.vmobjects.SObjectWithClass;
import som.vmobjects.SSymbol;
import tools.concurrency.TracingActors.ReplayActor;
+import tools.concurrency.TracingActors.TracingActor;
import tools.concurrency.TracingBackend;
import tools.language.StructuralProbe;
+import tools.snapshot.deserialization.DeserializationBuffer;
+import tools.snapshot.deserialization.SnapshotParser;
public class SnapshotBackend {
private static byte snapshotVersion = 0;
- private static final EconomicMap symbolDictionary;
- private static final EconomicMap classDictionary;
- private static final StructuralProbe probe;
- private static final ConcurrentLinkedQueue buffers;
+ private static final EconomicMap symbolDictionary;
+ private static final EconomicMap classDictionary;
+ private static final StructuralProbe probe;
+ private static final ConcurrentLinkedQueue buffers;
+ private static final ConcurrentLinkedQueue> messages;
+ private static final EconomicMap classLocations;
+ private static final ConcurrentHashMap deferredSerializations;
+
+ private static final ArrayList lostResolutions;
+
+ // this is a reference to the list maintained by the objectsystem
+ private static EconomicMap loadedModules;
+ private static SPromise resultPromise;
+ @CompilationFinal private static VM vm;
static {
if (VmSettings.TRACK_SNAPSHOT_ENTITIES) {
@@ -33,19 +72,39 @@ public class SnapshotBackend {
symbolDictionary = EconomicMap.create();
probe = new StructuralProbe();
buffers = new ConcurrentLinkedQueue<>();
+ messages = new ConcurrentLinkedQueue<>();
+ deferredSerializations = new ConcurrentHashMap<>();
+ lostResolutions = new ArrayList<>();
+ classLocations = null;
+ // identity int, includes mixin info
+ // long outer
+ // essentially this is about capturing the outer
+ // let's do this when the class is stucturally initialized
} else if (VmSettings.SNAPSHOTS_ENABLED) {
classDictionary = null;
symbolDictionary = null;
probe = null;
+ classLocations = EconomicMap.create();
buffers = new ConcurrentLinkedQueue<>();
+ messages = new ConcurrentLinkedQueue<>();
+ deferredSerializations = new ConcurrentHashMap<>();
+ lostResolutions = new ArrayList<>();
} else {
classDictionary = null;
symbolDictionary = null;
probe = null;
+ classLocations = null;
buffers = null;
+ messages = null;
+ deferredSerializations = null;
+ lostResolutions = null;
}
}
+ public static void initialize(final VM vm) {
+ SnapshotBackend.vm = vm;
+ }
+
public static SSymbol getSymbolForId(final short id) {
return symbolDictionary.get(id);
}
@@ -55,33 +114,162 @@ public static void registerSymbol(final SSymbol sym) {
symbolDictionary.put(sym.getSymbolId(), sym);
}
- public static void registerClass(final SSymbol sym, final SClass clazz) {
+ public static synchronized void registerClass(final SClass clazz) {
assert VmSettings.TRACK_SNAPSHOT_ENTITIES;
- classDictionary.put(sym, clazz);
+ classDictionary.put(clazz.getIdentity(), clazz);
}
- public static SClass lookupClass(final SSymbol sym) {
- assert VmSettings.TRACK_SNAPSHOT_ENTITIES;
- return classDictionary.get(sym);
+ public static void registerLoadedModules(final EconomicMap loaded) {
+ loadedModules = loaded;
}
- public static SClass lookupClass(final short sym) {
+ public static SClass lookupClass(final int id) {
assert VmSettings.TRACK_SNAPSHOT_ENTITIES;
- return classDictionary.get(getSymbolForId(sym));
+ if (classDictionary.containsKey(id)) {
+ if (!(classDictionary.get(id) instanceof SClass)) {
+ return null;
+ }
+
+ return (SClass) classDictionary.get(id);
+ }
+
+ // doesn't exist yet
+ return createSClass(id);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static SClass lookupClass(final int id, final long ref) {
+ if (classDictionary.containsKey(id)) {
+ Object entry = classDictionary.get(id);
+ if (entry instanceof SClass) {
+ return (SClass) entry;
+ }
+
+ LinkedList todo = null;
+ if (entry == null) {
+ todo = new LinkedList();
+ classDictionary.put(id, todo);
+ } else if (entry instanceof LinkedList>) {
+ todo = (LinkedList) entry;
+ }
+ todo.add(ref);
+ return null;
+ }
+
+ // doesn't exist yet
+ return createSClass(id);
+ }
+
+ private static SClass createSClass(final int id) {
+ MixinDefinition mixin = acquireMixin(id);
+
+ // Step 1: install placeholder
+ classDictionary.put(id, null);
+ // Output.println("creating Class" + mixin.getIdentifier() + " : " + (short) id);
+
+ // Step 2: get outer object
+ SObjectWithClass enclosingObject = SnapshotParser.getOuterForClass(id);
+ assert enclosingObject != null;
+
+ // Step 3: create Class
+ Object superclassAndMixins =
+ mixin.getSuperclassAndMixinResolutionInvokable().createCallTarget()
+ .call(new Object[] {enclosingObject});
+
+ ClassFactory factory = mixin.createClassFactory(superclassAndMixins, false, false, false);
+
+ SClass result = new SClass(enclosingObject,
+ ClassInstantiationNode.instantiateMetaclassClass(factory, enclosingObject));
+ factory.initializeClass(result);
+
+ // Step 4: fixup
+ Object current = classDictionary.get(id);
+ classDictionary.put(id, result);
+
+ if (current instanceof LinkedList) {
+ @SuppressWarnings("unchecked")
+ LinkedList todo = (LinkedList) current;
+ DeserializationBuffer db = SnapshotParser.getDeserializationBuffer();
+ for (long ref : todo) {
+ db.putAndFixUpIfNecessary(ref, result);
+ }
+ }
+ return result;
+ }
+
+ private static MixinDefinition acquireMixin(final int id) {
+ short symId = (short) (id >> 16);
+
+ SSymbol location = getSymbolForId(symId);
+ String[] parts = location.getString().split(":");
+ if (parts.length != 2) {
+ assert false;
+ }
+
+ Path path = Paths.get(VmSettings.BASE_DIRECTORY, parts[0]);
+ MixinDefinition mixin = loadedModules.get(path.toUri());
+
+ if (mixin == null) {
+ // need to load module
+ try {
+ mixin = vm.loadModule(path.toString());
+ SClass module = mixin.instantiateModuleClass();
+ classDictionary.put(module.getIdentity(), module);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ String[] nestings = parts[1].split("\\.");
+
+ for (String sub : nestings) {
+ MixinDefinition temp = mixin.getNestedMixinDefinition(sub);
+ if (temp != null) {
+ mixin = temp;
+ }
+ }
+ return mixin;
}
public static SInvokable lookupInvokable(final SSymbol sym) {
assert VmSettings.TRACK_SNAPSHOT_ENTITIES;
- return probe.lookupMethod(sym);
+ SInvokable result = probe.lookupMethod(sym);
+
+ if (result == null) {
+ // Module probably not loaded, attempt to do that.
+ String[] parts = sym.getString().split(":");
+ Path path = Paths.get(VmSettings.BASE_DIRECTORY, parts[0]);
+
+ MixinDefinition mixin;
+ mixin = loadedModules.get(path.toUri());
+ if (mixin == null) {
+ // need to load module
+ try {
+ mixin = vm.loadModule(path.toString());
+ SClass module = mixin.instantiateModuleClass();
+ classDictionary.put(module.getIdentity(), module);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ result = probe.lookupMethod(sym);
+ return result;
}
public static SInvokable lookupInvokable(final short sym) {
- assert VmSettings.TRACK_SNAPSHOT_ENTITIES;
- return probe.lookupMethod(getSymbolForId(sym));
+ return lookupInvokable(getSymbolForId(sym));
}
public static synchronized void startSnapshot() {
assert VmSettings.SNAPSHOTS_ENABLED;
+ deferredSerializations.clear();
+ lostResolutions.clear();
+ buffers.clear();
+ synchronized (classLocations) {
+ classLocations.clear();
+ }
snapshotVersion++;
// notify the worker in the tracingbackend about this change.
@@ -97,20 +285,40 @@ public static byte getSnapshotVersion() {
public static Actor lookupActor(final int actorId) {
if (VmSettings.REPLAY) {
- return ReplayActor.getActorWithId(actorId);
+ ReplayActor ra = ReplayActor.getActorWithId(actorId);
+ if (ra == null) {
+ ra = new ReplayActor(vm, actorId);
+ }
+ return ra;
} else {
// For testing with snaphsotClone:
return EventualMessage.getActorCurrentMessageIsExecutionOn();
}
}
- public static void registerSnapshotBuffer(final SnapshotBuffer sb) {
+ public static void registerSnapshotBuffer(final SnapshotBuffer sb,
+ final ArrayList messageLocations) {
if (VmSettings.TEST_SERIALIZE_ALL) {
return;
}
assert sb != null;
buffers.add(sb);
+ messages.add(messageLocations);
+ }
+
+ /**
+ * Serialization of objects referenced from far references need to be deferred to the owning
+ * actor.
+ */
+ @TruffleBoundary
+ public static void deferSerialization(final SnapshotRecord sr) {
+ deferredSerializations.put(sr, 0);
+ }
+
+ @TruffleBoundary
+ public static void completedSerialization(final SnapshotRecord sr) {
+ deferredSerializations.remove(sr);
}
public static StructuralProbe getProbe() {
@@ -118,21 +326,218 @@ public static StructuralProbe getProbe() {
return probe;
}
+ public static TracingActor getCurrentActor() {
+ if (VmSettings.REPLAY) {
+ return SnapshotParser.getCurrentActor();
+ } else {
+ return (TracingActor) EventualMessage.getActorCurrentMessageIsExecutionOn();
+ }
+ }
+
+ /**
+ * Persist the current snapshot to a file.
+ */
+ private static final ClassPrim classPrim = ClassPrimFactory.create(null);
+
public static void writeSnapshot() {
if (buffers.size() == 0) {
return;
}
- String name = VmSettings.TRACE_FILE + snapshotVersion;
+ String name = VmSettings.TRACE_FILE + '.' + snapshotVersion;
File f = new File(name + ".snap");
try (FileOutputStream fos = new FileOutputStream(f)) {
+ // Write Message Locations
+ int offset = writeMessageLocations(fos);
+ offset += writeClassEnclosures(fos);
+ offset += writeLostResolutions(fos);
+
+ // handle the unfinished serialization.
+ SnapshotBuffer buffer = buffers.peek();
+
+ while (!deferredSerializations.isEmpty()) {
+ for (SnapshotRecord sr : deferredSerializations.keySet()) {
+ assert sr.owner != null;
+ deferredSerializations.remove(sr);
+ buffer.owner.setCurrentActorForSnapshot(sr.owner);
+ sr.handleObjectsReferencedFromFarRefs(buffer, classPrim);
+ }
+ }
+
+ writeSymbolTable();
+
+ // WriteHeapMap
+ writeHeapMap(fos, offset);
+
+ // Write Heap
while (!buffers.isEmpty()) {
SnapshotBuffer sb = buffers.poll();
fos.getChannel().write(ByteBuffer.wrap(sb.getRawBuffer(), 0, sb.position()));
fos.flush();
}
+
} catch (IOException e1) {
throw new RuntimeException(e1);
}
}
+
+ private static int writeClassEnclosures(final FileOutputStream fos) throws IOException {
+ int size = (classLocations.size() * 2 + 1) * Long.BYTES;
+ ByteBuffer bb = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
+
+ bb.putLong(classLocations.size());
+
+ MapCursor cursor = classLocations.getEntries();
+ while (cursor.advance()) {
+ bb.putLong(cursor.getKey());
+ bb.putLong(cursor.getValue());
+ }
+
+ bb.rewind();
+ fos.getChannel().write(bb);
+ fos.flush();
+ return size;
+ }
+
+ /**
+ * This method creates a list that allows us to know where a {@link SnapshotBuffer} starts in
+ * the file.
+ */
+ private static void writeHeapMap(final FileOutputStream fos, final int msgSize)
+ throws IOException {
+ // need to have a registry of the different heap areas
+ int numBuffers = buffers.size();
+ int registrySize = ((numBuffers * 2 + 2) * Long.BYTES);
+ ByteBuffer bb = ByteBuffer.allocate(registrySize).order(ByteOrder.LITTLE_ENDIAN);
+ // get and write location of the promise
+ TracingActor ta = (TracingActor) resultPromise.getOwner();
+ SPromise.getPromiseClass().serialize(resultPromise, buffers.peek());
+ long location = ta.getSnapshotRecord().getObjectPointer(resultPromise);
+ bb.putLong(location);
+
+ bb.putLong(numBuffers);
+
+ int bufferStart = msgSize + registrySize;
+ for (SnapshotBuffer sb : buffers) {
+ long id = sb.owner.getThreadId();
+ bb.putLong(id);
+ bb.putLong(bufferStart);
+ bufferStart += sb.position();
+ }
+
+ bb.rewind();
+ fos.getChannel().write(bb);
+ }
+
+ /**
+ * This method persists the locations of messages in mailboxes, i.e. the roots of our object
+ * graph
+ */
+ private static int writeMessageLocations(final FileOutputStream fos)
+ throws IOException {
+ int entryCount = 0;
+ for (ArrayList al : messages) {
+ assert al.size() % 2 == 0;
+ entryCount += al.size();
+ }
+
+ int msgSize = ((entryCount + 1) * Long.BYTES);
+ ByteBuffer bb =
+ ByteBuffer.allocate(msgSize).order(ByteOrder.LITTLE_ENDIAN);
+ bb.putLong(entryCount);
+ for (ArrayList al : messages) {
+ for (long l : al) {
+ bb.putLong(l);
+ }
+ }
+
+ bb.rewind();
+ fos.getChannel().write(bb);
+ return msgSize;
+ }
+
+ private static int writeLostResolutions(final FileOutputStream fos) throws IOException {
+ int entryCount = 0;
+ for (ArrayList al : messages) {
+ assert al.size() % 2 == 0;
+ entryCount += al.size();
+ }
+
+ int size = (lostResolutions.size() + 1) * Long.BYTES;
+ ByteBuffer bb =
+ ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
+ bb.putLong(lostResolutions.size());
+ for (long l : lostResolutions) {
+ bb.putLong(l);
+ }
+
+ bb.rewind();
+ fos.getChannel().write(bb);
+ return size;
+ }
+
+ private static void writeSymbolTable() {
+ Collection symbols = Symbols.getSymbols();
+
+ if (symbols.isEmpty()) {
+ return;
+ }
+ File f = new File(VmSettings.TRACE_FILE + ".sym");
+
+ try (FileOutputStream symbolStream = new FileOutputStream(f);
+ BufferedWriter symbolWriter =
+ new BufferedWriter(new OutputStreamWriter(symbolStream))) {
+
+ for (SSymbol s : symbols) {
+ symbolWriter.write(s.getSymbolId() + ":" + s.getString());
+ symbolWriter.newLine();
+ }
+ symbolWriter.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerResultPromise(final SPromise promise) {
+ assert resultPromise == null;
+ resultPromise = promise;
+ }
+
+ public static void registerLostResolution(final SResolver resolver,
+ final SnapshotBuffer sb) {
+
+ SResolver.getResolverClass().serialize(resolver, sb);
+
+ if (!resolver.getPromise().isCompleted()) {
+ Output.println("skipped!!");
+ return;
+ }
+
+ Object result = resolver.getPromise().getValueForSnapshot();
+
+ Types.getClassOf(result).serialize(result, sb);
+
+ int base = sb.reserveSpace(Long.BYTES * 2 + Integer.BYTES + 1);
+ synchronized (lostResolutions) {
+ lostResolutions.add(sb.calculateReference(base));
+ }
+
+ sb.putLongAt(base, sb.getRecord().getObjectPointer(resolver));
+ sb.putLongAt(base + Long.BYTES, sb.getRecord().getObjectPointer(result));
+ sb.putIntAt(base + Long.BYTES * 2,
+ ((TracingActor) sb.getOwner().getCurrentActor()).getActorId());
+ sb.putByteAt(base + Long.BYTES * 2 + Integer.BYTES,
+ (byte) resolver.getPromise().getResolutionStateUnsync().ordinal());
+
+ }
+
+ public static void registerClassLocation(final int identity, final long classLocation) {
+ if (VmSettings.TEST_SNAPSHOTS) {
+ return;
+ }
+ synchronized (classLocations) {
+ classLocations.put(identity, classLocation);
+ }
+
+ }
}
diff --git a/src/tools/snapshot/SnapshotBuffer.java b/src/tools/snapshot/SnapshotBuffer.java
index 7e63417fa..883263514 100644
--- a/src/tools/snapshot/SnapshotBuffer.java
+++ b/src/tools/snapshot/SnapshotBuffer.java
@@ -1,9 +1,13 @@
package tools.snapshot;
+import com.oracle.truffle.api.CompilerDirectives;
+
+import som.interpreter.SomLanguage;
import som.interpreter.actors.Actor.ActorProcessingThread;
-import som.interpreter.objectstorage.ClassFactory;
+import som.interpreter.actors.EventualMessage;
import som.vm.VmSettings;
import som.vm.constants.Classes;
+import som.vmobjects.SClass;
import tools.concurrency.TraceBuffer;
import tools.concurrency.TracingActors.TracingActor;
import tools.replay.nodes.TraceActorContextNode;
@@ -13,7 +17,7 @@
public class SnapshotBuffer extends TraceBuffer {
public static final int FIELD_SIZE = 8;
- public static final int CLASS_ID_SIZE = 2;
+ public static final int CLASS_ID_SIZE = 4;
public static final int MAX_FIELD_CNT = Byte.MAX_VALUE;
public static final int THREAD_SHIFT = Long.SIZE - Short.SIZE;
@@ -27,7 +31,8 @@ public SnapshotBuffer(final ActorProcessingThread owner) {
}
public SnapshotRecord getRecord() {
- return ((TracingActor) owner.getCurrentActor()).getSnapshotRecord();
+ return CompilerDirectives.castExact(owner.getCurrentActor(), TracingActor.class)
+ .getSnapshotRecord();
}
public ActorProcessingThread getOwner() {
@@ -38,40 +43,43 @@ public final long calculateReference(final long start) {
return (owner.getThreadId() << THREAD_SHIFT) | start;
}
- public int addObject(final Object o, final ClassFactory classFact, final int payload) {
- assert !getRecord().containsObject(o) : "Object serialized multiple times";
-
+ public int reserveSpace(final int bytes) {
int oldPos = this.position;
- getRecord().addObjectEntry(o, calculateReference(oldPos));
-
- this.putShortAt(this.position,
- classFact.getIdentifier().getSymbolId());
- this.position += CLASS_ID_SIZE + payload;
- return oldPos + CLASS_ID_SIZE;
+ this.position += bytes;
+ return oldPos;
}
- public int addObjectWithFields(final Object o, final ClassFactory classFact,
- final int fieldCnt) {
- assert fieldCnt < MAX_FIELD_CNT;
- assert !getRecord().containsObject(o) : "Object serialized multiple times";
+ public int addObject(final Object o, final SClass clazz, final int payload) {
+ assert !getRecord().containsObjectUnsync(o) : "Object serialized multiple times";
int oldPos = this.position;
getRecord().addObjectEntry(o, calculateReference(oldPos));
- this.putShortAt(this.position,
- classFact.getIdentifier().getSymbolId());
- this.position += CLASS_ID_SIZE + (FIELD_SIZE * fieldCnt);
+ if (clazz.getSOMClass() == Classes.classClass) {
+ TracingActor owner = clazz.getOwnerOfOuter();
+ if (owner == null) {
+ owner = (TracingActor) SomLanguage.getCurrent().getVM().getMainActor();
+ }
+
+ assert owner != null;
+ owner.getSnapshotRecord().farReference(clazz, null, 0);
+ }
+ this.putIntAt(this.position, clazz.getIdentity());
+ this.position += CLASS_ID_SIZE + payload;
return oldPos + CLASS_ID_SIZE;
}
- public int addMessage(final int payload) {
+ public int addMessage(final int payload, final EventualMessage msg) {
// we dont put messages into our lookup table as there should be only one reference to it
// (either from a promise or a mailbox)
int oldPos = this.position;
- getRecord().addMessageEntry(calculateReference(oldPos));
+ TracingActor ta = (TracingActor) owner.getCurrentActor();
+ assert !getRecord().containsObjectUnsync(
+ msg) : "Message serialized twice, and on the same actor";
+ getRecord().addObjectEntry(msg, calculateReference(oldPos));
+ // owner.addMessageLocation(ta.getActorId(), calculateReference(oldPos));
- this.putShortAt(this.position,
- Classes.messageClass.getFactory().getClassName().getSymbolId());
+ this.putIntAt(this.position, Classes.messageClass.getIdentity());
this.position += CLASS_ID_SIZE + payload;
return oldPos + CLASS_ID_SIZE;
}
diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java
index ee78aa1ed..6b1639368 100644
--- a/src/tools/snapshot/SnapshotRecord.java
+++ b/src/tools/snapshot/SnapshotRecord.java
@@ -3,9 +3,14 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import org.graalvm.collections.EconomicMap;
-import org.graalvm.collections.EconomicSet;
-import som.interpreter.Types;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+import som.interpreter.actors.EventualMessage.PromiseMessage;
+import som.primitives.ObjectPrims.ClassPrim;
+import som.vmobjects.SClass;
+import tools.concurrency.TracingActivityThread;
+import tools.concurrency.TracingActors.TracingActor;
public class SnapshotRecord {
@@ -14,7 +19,9 @@ public class SnapshotRecord {
* We can get the location of the serialized object in the trace
*/
private final EconomicMap