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 serializerFactory) { - initializeClass(result, superclassAndMixins, false, false, false, serializerFactory); + final NodeFactory 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 serializerFactory) { + final NodeFactory 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 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 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 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 getSerializerFactory() { + // serializerFactory can be null for Classes.messageClass + return serializerFactory; + } + + public void customizeSerialization( + final NodeFactory 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 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 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 serializerFactory) { + final NodeFactory 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 factory, + final AbstractSerializationNode deserializer) { + instanceClassGroup.customizeSerialization(factory, deserializer); + } + + public NodeFactory 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 entries; - private final EconomicSet messageOffsets; + protected final TracingActor owner; + private int msgCnt; + private int snapshotVersion; /** * This list is used to keep track of references to unserialized objects in the actor owning @@ -25,18 +32,38 @@ public class SnapshotRecord { * SnapshotBuffer is then known and used to fix the reference (writing a long in another * buffer at a specified location). */ - private final ConcurrentLinkedQueue externalReferences; + private ConcurrentLinkedQueue externalReferences; - public SnapshotRecord() { + public SnapshotRecord(final TracingActor owner) { this.entries = EconomicMap.create(); - this.messageOffsets = EconomicSet.create(); this.externalReferences = new ConcurrentLinkedQueue<>(); + this.owner = owner; + this.snapshotVersion = SnapshotBackend.getSnapshotVersion(); + msgCnt = 0; } - public boolean containsObject(final Object o) { + public long getMessageIdentifier() { + long result = (((long) owner.getActorId()) << 32) | msgCnt; + msgCnt++; + return result; + } + + /** + * only use this in the actor that owns this record (only the owner adds entries). + */ + @TruffleBoundary + public boolean containsObjectUnsync(final Object o) { return entries.containsKey(o); } + @TruffleBoundary + public boolean containsObject(final Object o) { + synchronized (entries) { + return entries.containsKey(o); + } + } + + @TruffleBoundary public long getObjectPointer(final Object o) { if (entries.containsKey(o)) { return entries.get(o); @@ -45,26 +72,31 @@ public long getObjectPointer(final Object o) { "Cannot point to unserialized Objects, you are missing a serialization call: " + o); } - public void addMessageEntry(final long offset) { - this.messageOffsets.add(offset); - } - + @TruffleBoundary public void addObjectEntry(final Object o, final long offset) { synchronized (entries) { entries.put(o, offset); } } - public void handleTodos(final SnapshotBuffer sb) { + @TruffleBoundary // TODO: convert to an approach that constructs a cache + public void handleObjectsReferencedFromFarRefs(final SnapshotBuffer sb, + final ClassPrim classPrim) { + // SnapshotBackend.removeTodo(this); while (!externalReferences.isEmpty()) { - FarRefTodo frt = externalReferences.poll(); + DeferredFarRefSerialization frt = externalReferences.poll(); + assert frt != null; // ignore todos from a different snapshot - if (frt.referer.snapshotVersion == sb.snapshotVersion) { - if (!this.containsObject(frt.target)) { - Types.getClassOf(frt.target).serialize(frt.target, sb); + if (frt.isCurrent()) { + if (!this.containsObjectUnsync(frt.target)) { + if (frt.target instanceof PromiseMessage) { + ((PromiseMessage) frt.target).forceSerialize(sb); + } else { + SClass clazz = classPrim.executeEvaluated(frt.target); + clazz.serialize(frt.target, sb); + } } - frt.resolve(getObjectPointer(frt.target)); } } @@ -81,32 +113,81 @@ public void handleTodos(final SnapshotBuffer sb) { */ public void farReference(final Object o, final SnapshotBuffer other, final int destination) { - Long l; - synchronized (entries) { - l = entries.get(o); + Long l = getEntrySynced(o); + + if (l != null && other != null) { + other.putLongAt(destination, l); + } else if (l == null) { + if (externalReferences.isEmpty()) { + SnapshotBackend.deferSerialization(this); + } + externalReferences.offer(new DeferredFarRefSerialization(other, destination, o)); } + } + + public void farReferenceMessage(final PromiseMessage pm, final SnapshotBuffer other, + final int destination) { + Long l = getEntrySynced(pm); if (l != null) { other.putLongAt(destination, l); } else { - externalReferences.offer(new FarRefTodo(other, destination, o)); + if (externalReferences.isEmpty()) { + SnapshotBackend.deferSerialization(this); + } + externalReferences.offer(new DeferredFarRefSerialization(other, destination, pm)); + } + } + + @TruffleBoundary + private Long getEntrySynced(final Object o) { + Long l; + synchronized (entries) { + l = entries.get(o); + } + return l; + } + + public int getSnapshotVersion() { + return snapshotVersion; + } + + public void resetRecordifNecessary(final int newVersion) { + if (this.snapshotVersion == newVersion) { + return; + } + + synchronized (entries) { + this.entries.clear(); } + + this.msgCnt = 0; + this.snapshotVersion = newVersion; } - private final class FarRefTodo { - private final SnapshotBuffer referer; - private final int referenceOffset; - final Object target; + public static final class DeferredFarRefSerialization { + final Object target; + final SnapshotBuffer referer; + final int referenceOffset; - FarRefTodo(final SnapshotBuffer referer, final int referenceOffset, + DeferredFarRefSerialization(final SnapshotBuffer referer, final int referenceOffset, final Object target) { + this.target = target; this.referer = referer; this.referenceOffset = referenceOffset; - this.target = target; } public void resolve(final long targetOffset) { - referer.putLongAt(referenceOffset, targetOffset); + if (referer != null) { + referer.putLongAt(referenceOffset, targetOffset); + } + } + + public boolean isCurrent() { + if (referer == null || !(Thread.currentThread() instanceof TracingActivityThread)) { + return true; + } + return referer.snapshotVersion == TracingActivityThread.currentThread().getSnapshotId(); } } } diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index afffa4a67..577ea10fe 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -1,91 +1,222 @@ package tools.snapshot.deserialization; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.ArrayList; import org.graalvm.collections.EconomicMap; -import som.interpreter.actors.Actor; +import som.interpreter.nodes.dispatch.CachedSlotWrite; +import som.vm.VmSettings; import som.vmobjects.SClass; +import som.vmobjects.SInvokable; +import som.vmobjects.SObject; import tools.snapshot.SnapshotBackend; import tools.snapshot.deserialization.FixupInformation.FixupList; +import tools.snapshot.nodes.ObjectSerializationNodes.SObjectSerializationNode.SlotFixup; +import tools.snapshot.nodes.PrimitiveSerializationNodes.ClassSerializationNode; public class DeserializationBuffer { - private final ByteBuffer wrapped; + protected final ByteBuffer buffer; private final EconomicMap deserialized; private long lastRef; + int depth = 0; + private ArrayList unserialized; public DeserializationBuffer(final byte[] backing) { - wrapped = ByteBuffer.wrap(backing).asReadOnlyBuffer().order(ByteOrder.LITTLE_ENDIAN); - wrapped.rewind(); + buffer = ByteBuffer.wrap(backing).asReadOnlyBuffer().order(ByteOrder.LITTLE_ENDIAN); + buffer.rewind(); deserialized = EconomicMap.create(); + unserialized = new ArrayList<>(); + } + + public DeserializationBuffer(final ByteBuffer buffer) { + this.buffer = buffer; + deserialized = EconomicMap.create(); + unserialized = new ArrayList<>(); } public byte get() { - return wrapped.get(); + return buffer.get(); } public void get(final byte[] b) { - wrapped.get(b); + buffer.get(b); } public short getShort() { - return wrapped.getShort(); + return buffer.getShort(); } public int getInt() { - return wrapped.getInt(); + return buffer.getInt(); } public long getLong() { - return wrapped.getLong(); + return buffer.getLong(); } public double getDouble() { - return wrapped.getDouble(); + return buffer.getDouble(); + } + + public boolean allreadyDeserialized(final long reference) { + return deserialized.containsKey(reference); + } + + public int getNumObjects() { + return deserialized.size(); + } + + private void printPosition(final long current) { + // Output.print(depth + " - " + getAbsolute(current) + " in " + (current >> 48) + " "); + } + + public static long getAbsolute(final long current) { + long pos = (int) current; + if (!VmSettings.TEST_SNAPSHOTS) { + pos += SnapshotParser.getFileOffset(current); + } + return pos; + } + + private void printClass(final int cId) { + // SSymbol sym = SnapshotBackend.getSymbolForId((short) (cId >> 16)); + // Output.println( + // " " + sym.getString() + ": " + // + ((short) cId)); } public Object deserialize(final long current) { - assert !deserialized.containsKey(current); - this.position((int) current); + long backup = lastRef; + long previous = this.position(); + Object result = deserializeWithoutContext(current); + this.position(previous); + lastRef = backup; + return result; + } + + public long readOuterForClass(final long classLocation) { + long previous = this.position(); + this.position(classLocation); + this.getInt(); // consume the classclass information + long result = ClassSerializationNode.readOuterLocation(this); + this.position(previous); + return result; + } + + public Object deserializeWithoutContext(final long current) { + if (deserialized.containsKey(current)) { + Object o = deserialized.get(current); + if (!needsFixup(o)) { + return o; + } + } else { + // to avoid endless loop, when null is read we replace it with a linked list containing + // fixup information + deserialized.put(current, null); + } + + lastRef = current; + printPosition(current); + this.position(current); + + int cId = getInt(); + printClass(cId); + + depth++; + SClass clazz = SnapshotBackend.lookupClass(cId); + if (clazz == null) { + unserialized.add(current); + depth--; + return null; + } - // to avoid endless loop, when null is read we replace it with a linked list containing - // fixup information - deserialized.put(current, null); - short cId = getShort(); - Object o = SnapshotBackend.lookupClass(cId).getSerializer().deserialize(this); + Object o = clazz.deserialize(this); - fixUpIfNecessary(current, o); - deserialized.put(current, o); + depth--; + if (o != null) { + putAndFixUpIfNecessary(current, o); + } return o; } + /** + * This causes the lastRef to stay overwritten for fixup purposes! + * + * @return + */ public Object getReference() { long reference = getLong(); lastRef = reference; + if (!deserialized.containsKey(reference)) { - int current = position(); + long current = position(); + printPosition(reference); + + deserialized.put(reference, null); + + // prepare deserialize referenced object + position(reference); + int cId = getInt(); + printClass(cId); + + depth++; + SClass clazz = SnapshotBackend.lookupClass(cId); + if (clazz == null) { + unserialized.add(reference); + depth--; + position(current); + return null; + } + + Object o = clazz.deserialize(this); + depth--; + // continue with current object + position(current); + if (o != null) { + putAndFixUpIfNecessary(reference, o); + } + return o; + } else { + printPosition(reference); + return deserialized.get(reference); + } + } + public Object getMaterializedFrame(final SInvokable invokable) { + long reference = getLong(); + + lastRef = reference; + if (!deserialized.containsKey(reference)) { + long current = position(); deserialized.put(reference, null); // prepare deserialize referenced object - position((int) reference); - short classId = getShort(); - SClass clazz = SnapshotBackend.lookupClass(classId); - Object o = clazz.getSerializer().deserialize(this); + position(reference); + // need to read the class ID even though it's actually unused + getInt(); + depth++; + Object o = invokable.getFrameSerializer().deserialize(this); + depth--; // continue with current object position(current); - fixUpIfNecessary(reference, o); - deserialized.put(reference, o); + putAndFixUpIfNecessary(reference, o); return o; } else { return deserialized.get(reference); } } + public Object getReference(final long location) { + return deserialized.get(location); + } + public static boolean needsFixup(final Object o) { return o == null || o instanceof FixupList; } @@ -99,8 +230,11 @@ public synchronized void installFixup(final FixupInformation fi) { } } - private synchronized void fixUpIfNecessary(final long reference, final Object result) { + public synchronized void putAndFixUpIfNecessary(final long reference, final Object result) { + assert result != null; Object ref = deserialized.get(reference); + deserialized.put(reference, result); + if (ref instanceof FixupList) { // we have fixup information, this means that this object is part of a circular // relationship @@ -110,15 +244,111 @@ private synchronized void fixUpIfNecessary(final long reference, final Object re } } - public int position() { - return wrapped.position(); + public void doUnserialized() { + while (!unserialized.isEmpty()) { + ArrayList todo = unserialized; + unserialized = new ArrayList<>(); + + for (long ref : todo) { + deserializeWithoutContext(ref); + } + } + } + + public synchronized void putObject(final SObject o) { + putAndFixUpIfNecessary(lastRef, o); + } + + public synchronized void installObjectFixup(final SObject o, final CachedSlotWrite write) { + long backup = lastRef; + long reference = getLong(); + long current = position(); + lastRef = reference; + if (deserialized.containsKey(reference)) { + Object oo = deserialized.get(reference); + if (needsFixup(oo)) { + installFixup(new SlotFixup(o, write)); + } else { + write.doWrite(o, deserialized.get(reference)); + } + } else { + installFixup(new SlotFixup(o, write)); + deserialize(reference); + } + lastRef = backup; + position(current); + } + + protected void ensureRemaining(final int bytes) + throws IOException { + assert buffer.remaining() >= bytes; + } + + public long position() { + return buffer.position(); } - public void position(final int newPosition) { - wrapped.position(newPosition); + public void position(final long newPosition) { + buffer.position((int) newPosition); } - public Actor getActor() { - return null; + public static class FileDeserializationBuffer extends DeserializationBuffer { + FileChannel channel; + // position inside a snapshotbuffer, where the current buffer contents start + long snapshotPosition; + + public FileDeserializationBuffer(final FileChannel channel) { + super(ByteBuffer.allocate(VmSettings.BUFFER_SIZE).order(ByteOrder.LITTLE_ENDIAN)); + buffer.limit(0); + this.channel = channel; + } + + @Override + protected void ensureRemaining(final int bytes) { + if (buffer.remaining() < bytes) { + // need to refill buffer + snapshotPosition += buffer.position(); + buffer.compact(); + try { + channel.read(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + buffer.flip(); + } + } + + @Override + public long position() { + return snapshotPosition + buffer.position(); + } + + @Override + public void position(final long newPosition) { + snapshotPosition = newPosition; + + // cut away the thread identification + // 0x FF FF FF FF FF FF + long offset = 0x0000FFFFFFFFFFFFL & newPosition; + + // absolute position in file + if (!VmSettings.TEST_SNAPSHOTS) { + offset += SnapshotParser.getFileOffset(newPosition); + } + + try { + assert offset <= channel.size() : "Reading beyond EOF"; + } catch (IOException e) { + throw new RuntimeException(e); + } + try { + channel.position(offset); + buffer.clear(); + channel.read(buffer); + buffer.flip(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } } diff --git a/src/tools/snapshot/deserialization/SnapshotParser.java b/src/tools/snapshot/deserialization/SnapshotParser.java new file mode 100644 index 000000000..52e712fb0 --- /dev/null +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -0,0 +1,313 @@ +package tools.snapshot.deserialization; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.PriorityQueue; + +import org.graalvm.collections.EconomicMap; + +import som.VM; +import som.interpreter.actors.EventualMessage; +import som.interpreter.actors.EventualMessage.PromiseMessage; +import som.interpreter.actors.SPromise; +import som.interpreter.actors.SPromise.Resolution; +import som.interpreter.actors.SPromise.SResolver; +import som.interpreter.actors.SPromise.STracingPromise; +import som.vm.Symbols; +import som.vm.VmSettings; +import som.vm.constants.Nil; +import som.vmobjects.SClass; +import som.vmobjects.SObjectWithClass; +import tools.concurrency.TracingActors.ReplayActor; +import tools.snapshot.SnapshotBackend; +import tools.snapshot.SnapshotBuffer; +import tools.snapshot.deserialization.DeserializationBuffer.FileDeserializationBuffer; + + +public final class SnapshotParser { + + private static SnapshotParser parser; + + private EconomicMap heapOffsets; + private EconomicMap> messageLocations; + private SPromise resultPromise; + private ReplayActor currentActor; + private VM vm; + private EconomicMap classLocations; + private DeserializationBuffer db; + private int objectcnt; + private HashSet sentPMsgs; + + private SnapshotParser(final VM vm) { + this.vm = vm; + this.heapOffsets = EconomicMap.create(); + this.messageLocations = EconomicMap.create(); + this.classLocations = EconomicMap.create(); + this.sentPMsgs = new HashSet<>(); + } + + // preparations to be done before anything else + public static void preparations() { + parseSymbols(); + } + + public static void inflate(final VM vm) { + if (parser == null) { + parser = new SnapshotParser(vm); + } + parser.parseMetaData(); + } + + /** + * Read the Method Pointers, their actorIds, and most importantly the start addresses for the + * thread areas. + */ + private void parseMetaData() { + ByteBuffer b = ByteBuffer.allocate(VmSettings.BUFFER_SIZE).order(ByteOrder.LITTLE_ENDIAN); + String fileName = VmSettings.TRACE_FILE + ".1.snap"; + File traceFile = new File(fileName); + try (FileInputStream fis = new FileInputStream(traceFile); + FileChannel channel = fis.getChannel()) { + channel.read(b); + b.flip(); // prepare for reading from buffer + + long numMessages = b.getLong() / 2; + for (int i = 0; i < numMessages; i++) { + ensureRemaining(Long.BYTES * 2, b, channel); + long messageIdentifier = b.getLong(); + int actorId = (int) (messageIdentifier >> 32); + int msgNo = (int) messageIdentifier; + long location = b.getLong(); + + if (!messageLocations.containsKey(actorId)) { + messageLocations.put(actorId, new PriorityQueue<>()); + } + messageLocations.get(actorId).add(new MessageLocation(msgNo, location)); + } + + long numOuters = b.getLong(); + for (int i = 0; i < numOuters; i++) { + ensureRemaining(Long.BYTES * 2, b, channel); + int identity = (int) b.getLong(); + long classLocation = b.getLong(); + classLocations.put(identity, classLocation); + } + + long numResolutions = b.getLong(); + ArrayList lostResolutions = new ArrayList<>(); + for (int i = 0; i < numResolutions; i++) { + ensureRemaining(Long.BYTES, b, channel); + long resolver = b.getLong(); + lostResolutions.add(resolver); + } + + ensureRemaining(Long.BYTES * 2, b, channel); + long resultPromiseLocation = b.getLong(); + long numHeaps = b.getLong(); + for (int i = 0; i < numHeaps; i++) { + ensureRemaining(Long.BYTES * 2, b, channel); + long threadId = b.getLong(); + long offset = b.getLong(); + heapOffsets.put(threadId, offset); + } + + // At this point we now have read all of the metadata and can begin the process of + // inflating the snapshot. + + // make sure all the actors exist, we resuse the mapping in ReplayActor + for (int id : messageLocations.getKeys()) { + SnapshotBackend.lookupActor(id); + } + + db = new FileDeserializationBuffer(channel); + ArrayList messagesNeedingFixup = new ArrayList<>(); + + // now let's go through the message list actor by actor, deserialize each message, and + // add it to the actors mailbox. + for (int id : messageLocations.getKeys()) { + PriorityQueue locations = messageLocations.get(id); + MessageLocation ml = locations.poll(); + while (ml != null) { + // Deserialilze message + currentActor = ReplayActor.getActorWithId(id); + EventualMessage em = (EventualMessage) db.deserializeWithoutContext(ml.location); + db.doUnserialized(); + + if (em instanceof PromiseMessage) { + if (em.getArgs()[0] instanceof SPromise) { + STracingPromise prom = (STracingPromise) ((PromiseMessage) em).getPromise(); + if (prom.isCompleted()) { + ((PromiseMessage) em).resolve(prom.getValueForSnapshot(), currentActor, + SnapshotBackend.lookupActor(prom.getResolvingActor())); + } else { + messagesNeedingFixup.add((PromiseMessage) em); + } + } + } + + if (em.getResolver() != null && em.getResolver().getPromise().isCompleted() + && em.getArgs()[0] instanceof SClass) { + SPromise cp = em.getResolver().getPromise(); + // need to unresolve this promise... + cp.unresolveFromSnapshot(Resolution.UNRESOLVED); + } + + currentActor.sendSnapshotMessage(em); + ml = locations.poll(); + } + } + + for (long entry : lostResolutions) { + db.position(entry); + long resolverLoc = db.getLong(); + long resultLoc = db.getLong(); + + int resolvingActor = db.getInt(); + byte resolutionState = db.get(); + + SResolver resolver = (SResolver) db.deserializeWithoutContext(resolverLoc); + Object result = db.deserializeWithoutContext(resultLoc); + + STracingPromise prom = (STracingPromise) resolver.getPromise(); + if (!prom.isCompleted()) { + prom.resolveFromSnapshot(result, Resolution.values()[resolutionState], + SnapshotBackend.lookupActor(resolvingActor), true); + prom.setResolvingActorForSnapshot(resolvingActor); + } + } + + for (PromiseMessage em : messagesNeedingFixup) { + STracingPromise prom = (STracingPromise) em.getPromise(); + if (em.getArgs()[0] instanceof SPromise) { + em.resolve(prom.getValueForSnapshot(), prom.getOwner(), + SnapshotBackend.lookupActor(prom.getResolvingActor())); + } + } + + resultPromise = (SPromise) db.getReference(resultPromiseLocation); + if (resultPromise == null) { + resultPromise = (SPromise) db.deserialize(resultPromiseLocation); + } + + classLocations = null; + messageLocations = null; + + assert resultPromise != null : "The result promise was not found"; + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // prevent usage after closing + objectcnt = db.getNumObjects(); + db = null; + } + } + + public static boolean addPMsg(final EventualMessage msg) { + return parser.sentPMsgs.add(msg); + } + + private static void parseSymbols() { + File symbolFile = new File(VmSettings.TRACE_FILE + ".sym"); + // create mapping from old to new symbol ids + try (FileInputStream fis = new FileInputStream(symbolFile); + BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { + String line = br.readLine(); + while (line != null) { + String[] a = line.split(":", 2); + Symbols.addSymbolFor(a[1], Short.parseShort(a[0])); + line = br.readLine(); + } + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static long getFileOffset(final long address) { + long threadId = address >> SnapshotBuffer.THREAD_SHIFT; + assert parser.heapOffsets.containsKey( + threadId) : "Probably some actor didn't get to finish it's todo list"; + return parser.heapOffsets.get(threadId); + } + + public static SObjectWithClass getOuterForClass(final int identity) { + SObjectWithClass result; + + if (parser.classLocations.containsKey(identity)) { + long reference = parser.db.readOuterForClass(parser.classLocations.get(identity)); + Object o = parser.db.getReference(reference); + if (!parser.db.allreadyDeserialized(reference)) { + result = (SObjectWithClass) parser.db.deserialize(reference); + } else if (DeserializationBuffer.needsFixup(o)) { + result = null; + // OuterFixup!! + } else { + result = (SObjectWithClass) o; + } + } else { + result = Nil.nilObject; + } + return result; + } + + private void ensureRemaining(final int bytes, final ByteBuffer b, final FileChannel channel) + throws IOException { + if (b.remaining() < bytes) { + // need to refill buffer + b.compact(); + channel.read(b); + b.flip(); + assert b.remaining() >= bytes; + } + } + + public static ReplayActor getCurrentActor() { + assert parser.currentActor != null; + return parser.currentActor; + } + + public static void setCurrentActor(final ReplayActor current) { + parser.currentActor = current; + } + + public static SPromise getResultPromise() { + assert parser.resultPromise != null; + return parser.resultPromise; + } + + public static DeserializationBuffer getDeserializationBuffer() { + return parser.db; + } + + public static int getObjectCnt() { + return parser.objectcnt; + } + + private class MessageLocation implements Comparable { + final int msgNo; + final long location; + + MessageLocation(final int msgNo, final long location) { + this.msgNo = msgNo; + this.location = location; + } + + @Override + public int compareTo(final MessageLocation o) { + return Integer.compare(msgNo, o.msgNo); + } + } +} diff --git a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java index 77d3c22eb..f0861a3cd 100644 --- a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java +++ b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java @@ -4,10 +4,10 @@ import com.oracle.truffle.api.dsl.Specialization; import som.interpreter.Types; -import som.interpreter.objectstorage.ClassFactory; import som.vm.constants.Classes; import som.vmobjects.SArray; import som.vmobjects.SArray.PartiallyEmptyArray; +import som.vmobjects.SClass; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; @@ -21,8 +21,10 @@ public abstract class AbstractArraySerializationNode extends AbstractSerializati private static final byte TYPE_OBJECT = 3; private static final byte TYPE_EMPTY = 4; - public AbstractArraySerializationNode(final ClassFactory classFact) { - super(classFact); + private final SClass clazz; + + public AbstractArraySerializationNode(final SClass clazz) { + this.clazz = clazz; } @Override @@ -35,7 +37,7 @@ public Object deserialize(final DeserializationBuffer sb) { protected void doBoolean(final SArray sa, final SnapshotBuffer sb) { boolean[] ba = sa.getBooleanStorage(); int requiredSpace = ba.length; - int base = sb.addObject(sa, classFact, requiredSpace + 5); + int base = sb.addObject(sa, clazz, requiredSpace + 5); sb.putByteAt(base, TYPE_BOOLEAN); sb.putIntAt(base + 1, ba.length); base += 5; @@ -49,7 +51,7 @@ protected void doBoolean(final SArray sa, final SnapshotBuffer sb) { protected void doDouble(final SArray sa, final SnapshotBuffer sb) { double[] da = sa.getDoubleStorage(); int requiredSpace = da.length * Double.BYTES; - int base = sb.addObject(sa, classFact, requiredSpace + 5); + int base = sb.addObject(sa, clazz, requiredSpace + 5); sb.putByteAt(base, TYPE_DOUBLE); sb.putIntAt(base + 1, da.length); base += 5; @@ -63,7 +65,7 @@ protected void doDouble(final SArray sa, final SnapshotBuffer sb) { protected void doLong(final SArray sa, final SnapshotBuffer sb) { long[] la = sa.getLongStorage(); int requiredSpace = la.length * Long.BYTES; - int base = sb.addObject(sa, classFact, requiredSpace + 5); + int base = sb.addObject(sa, clazz, requiredSpace + 5); sb.putByteAt(base, TYPE_LONG); sb.putIntAt(base + 1, la.length); base += 5; @@ -77,7 +79,7 @@ protected void doLong(final SArray sa, final SnapshotBuffer sb) { protected void doObject(final SArray sa, final SnapshotBuffer sb) { Object[] oa = sa.getObjectStorage(); int requiredSpace = oa.length * 8; - int base = sb.addObject(sa, classFact, requiredSpace + 5); + int base = sb.addObject(sa, clazz, requiredSpace + 5); sb.putByteAt(base, TYPE_OBJECT); sb.putIntAt(base + 1, oa.length); base += 5; @@ -91,7 +93,7 @@ protected void doObject(final SArray sa, final SnapshotBuffer sb) { @Specialization(guards = "sa.isEmptyType()") protected void doEmpty(final SArray sa, final SnapshotBuffer sb) { - int base = sb.addObject(sa, classFact, 5); + int base = sb.addObject(sa, clazz, 5); sb.putByteAt(base, TYPE_EMPTY); sb.putIntAt(base + 1, sa.getEmptyStorage()); } @@ -102,7 +104,7 @@ protected void doPartiallyEmpty(final SArray sa, final SnapshotBuffer sb) { Object[] oa = pea.getStorage(); int requiredSpace = oa.length * 8; - int base = sb.addObject(sa, classFact, requiredSpace + 5); + int base = sb.addObject(sa, clazz, requiredSpace + 5); sb.putByteAt(base, TYPE_OBJECT); sb.putIntAt(base + 1, oa.length); base += 5; @@ -155,6 +157,7 @@ protected Object parseBackingStorage(final DeserializationBuffer sb) { backing = oa; break; case TYPE_EMPTY: + backing = len; break; default: throw new IllegalArgumentException(); @@ -165,8 +168,8 @@ protected Object parseBackingStorage(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class ArraySerializationNode extends AbstractArraySerializationNode { - public ArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public ArraySerializationNode() { + super(Classes.arrayClass); } @Override @@ -177,10 +180,11 @@ public Object deserialize(final DeserializationBuffer sb) { } @GenerateNodeFactory - public abstract static class TransferArraySerializationNode extends ArraySerializationNode { + public abstract static class TransferArraySerializationNode + extends AbstractArraySerializationNode { - public TransferArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public TransferArraySerializationNode() { + super(Classes.transferArrayClass); } @Override @@ -191,10 +195,11 @@ public Object deserialize(final DeserializationBuffer sb) { } @GenerateNodeFactory - public abstract static class ValueArraySerializationNode extends ArraySerializationNode { + public abstract static class ValueArraySerializationNode + extends AbstractArraySerializationNode { - public ValueArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public ValueArraySerializationNode() { + super(Classes.valueArrayClass); } @Override diff --git a/src/tools/snapshot/nodes/AbstractSerializationNode.java b/src/tools/snapshot/nodes/AbstractSerializationNode.java index ae15bb702..1a2552302 100644 --- a/src/tools/snapshot/nodes/AbstractSerializationNode.java +++ b/src/tools/snapshot/nodes/AbstractSerializationNode.java @@ -2,21 +2,23 @@ import com.oracle.truffle.api.nodes.Node; -import som.interpreter.objectstorage.ClassFactory; import som.vm.VmSettings; +import som.vmobjects.SClass; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; public abstract class AbstractSerializationNode extends Node { - public final ClassFactory classFact; - public AbstractSerializationNode(final ClassFactory classFact) { + public AbstractSerializationNode() { assert VmSettings.SNAPSHOTS_ENABLED; - this.classFact = classFact; } public abstract void execute(Object o, SnapshotBuffer sb); - public abstract Object deserialize(DeserializationBuffer bb); + protected abstract Object deserialize(DeserializationBuffer bb); + + public Object deserialize(final DeserializationBuffer bb, final SClass clazz) { + return deserialize(bb); + } } diff --git a/src/tools/snapshot/nodes/BlockSerializationNode.java b/src/tools/snapshot/nodes/BlockSerializationNode.java index 88fa60431..52b82787d 100644 --- a/src/tools/snapshot/nodes/BlockSerializationNode.java +++ b/src/tools/snapshot/nodes/BlockSerializationNode.java @@ -1,17 +1,21 @@ package tools.snapshot.nodes; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; +import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.MaterializedFrame; import som.compiler.Variable.Internal; import som.interpreter.FrameOnStackMarker; -import som.interpreter.Method; import som.interpreter.Types; -import som.interpreter.objectstorage.ClassFactory; import som.vm.constants.Classes; import som.vmobjects.SAbstractObject; import som.vmobjects.SBlock; @@ -28,10 +32,6 @@ public abstract class BlockSerializationNode extends AbstractSerializationNode { private static final int SINVOKABLE_SIZE = Short.BYTES; - public BlockSerializationNode(final ClassFactory classFact) { - super(classFact); - } - // TODO specialize on different blocks @Specialization public void serialize(final SBlock block, final SnapshotBuffer sb) { @@ -39,23 +39,129 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { MaterializedFrame mf = block.getContextOrNull(); if (mf == null) { - int base = sb.addObject(block, classFact, SINVOKABLE_SIZE + 2); + int base = sb.addObject(block, Classes.blockClass, SINVOKABLE_SIZE + 1); SInvokable meth = block.getMethod(); sb.putShortAt(base, meth.getIdentifier().getSymbolId()); - sb.putShortAt(base + 2, (short) 0); + sb.putByteAt(base + 2, (byte) 0); } else { - FrameDescriptor fd = mf.getFrameDescriptor(); - - Object[] args = mf.getArguments(); - - int start = sb.addObject(block, classFact, - SINVOKABLE_SIZE + ((args.length + fd.getSlots().size()) * Long.BYTES) + 2); - int base = start; + int base = sb.addObject(block, Classes.blockClass, SINVOKABLE_SIZE + 1 + Long.BYTES); SInvokable meth = block.getMethod(); sb.putShortAt(base, meth.getIdentifier().getSymbolId()); - sb.putByteAt(base + 2, (byte) args.length); - base += 3; + sb.putByteAt(base + 2, (byte) 1); + + if (!sb.getRecord().containsObjectUnsync(mf)) { + meth.getFrameSerializer().execute(block, sb); + } + sb.putLongAt(base + 3, sb.getRecord().getObjectPointer(mf)); + } + } + + @Override + public Object deserialize(final DeserializationBuffer bb) { + short sinv = bb.getShort(); + byte framePresent = bb.get(); + + SInvokable invokable = SnapshotBackend.lookupInvokable(sinv); + assert invokable != null : "Invokable not found"; + + MaterializedFrame frame = null; + if (framePresent == 1) { + Object result = bb.getMaterializedFrame(invokable); + if (DeserializationBuffer.needsFixup(result)) { + SBlock block = new SBlock(invokable, null); + bb.installFixup(new BlockFrameFixup(block)); + return block; + } else { + frame = (MaterializedFrame) result; + } + } + return new SBlock(invokable, frame); + } + + public static class BlockFrameFixup extends FixupInformation { + SBlock block; + + public BlockFrameFixup(final SBlock block) { + this.block = block; + } + + @Override + public void fixUp(final Object o) { + try { + Field field = SBlock.class.getDeclaredField("context"); + field.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + field.set(block, o); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static class BlockArgumentFixup extends FixupInformation { + Object[] args; + int idx; + + public BlockArgumentFixup(final Object[] args, final int idx) { + this.args = args; + this.idx = idx; + } + + @Override + public void fixUp(final Object o) { + args[idx] = o; + } + } + + public static class FrameSlotFixup extends FixupInformation { + FrameSlot slot; + MaterializedFrame frame; + + public FrameSlotFixup(final MaterializedFrame frame, final FrameSlot slot) { + this.frame = frame; + this.slot = slot; + } + + @Override + public void fixUp(final Object o) { + frame.setObject(slot, o); + } + } + + @GenerateNodeFactory + public abstract static class FrameSerializationNode extends AbstractSerializationNode { + private final FrameDescriptor frameDescriptor; + + protected FrameSerializationNode(final FrameDescriptor frameDescriptor) { + this.frameDescriptor = frameDescriptor; + } + + @TruffleBoundary + private List getFrameslots() { + // TODO can we cache this? + return frameDescriptor.getSlots(); + } + + // Truffle doesn't seem to like me passing a frame, so we pass the entire block + @Specialization + public void serialize(final SBlock block, final SnapshotBuffer sb) { + MaterializedFrame frame = block.getContext(); + Object[] args = frame.getArguments(); + + List slots = getFrameslots(); + + int slotCnt = slots.size(); + assert slotCnt < 0xFF : "Too many slots"; + assert args.length < 0xFF : "Too many arguments"; + + int base = + sb.addObject(frame, Classes.frameClass, 2 + ((args.length + slotCnt) * Long.BYTES)); + + sb.putByteAt(base, (byte) args.length); + base++; SnapshotRecord record = sb.getRecord(); for (int i = 0; i < args.length; i++) { @@ -68,16 +174,17 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { int j = 0; - sb.putByteAt(base, (byte) fd.getSlots().size()); + sb.putByteAt(base, (byte) slotCnt); base++; - for (FrameSlot slot : fd.getSlots()) { + + for (FrameSlot slot : slots) { // assume this is ordered by index assert slot.getIndex() == j; // TODO optimization: MaterializedFrameSerialization Nodes that are associated with the // Invokables Frame Descriptor. Possibly use Local Var Read Nodes. - Object value = mf.getValue(slot); - switch (fd.getFrameSlotKind(slot)) { + Object value = frame.getValue(slot); + switch (frameDescriptor.getFrameSlotKind(slot)) { case Boolean: Classes.booleanClass.serialize(value, sb); break; @@ -99,7 +206,8 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { break; case Illegal: // Uninitialized variables - Types.getClassOf(fd.getDefaultValue()).serialize(fd.getDefaultValue(), sb); + Types.getClassOf(frameDescriptor.getDefaultValue()) + .serialize(frameDescriptor.getDefaultValue(), sb); break; default: throw new IllegalArgumentException("Unexpected SlotKind"); @@ -111,111 +219,77 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { // just serialize locals and arguments ordered by their slotnumber // we can get the frame from the invokables root node } - base += j * Long.BYTES; - assert base == start + SINVOKABLE_SIZE - + ((args.length + fd.getSlots().size()) * Long.BYTES) + 2; } - } - - @Override - public Object deserialize(final DeserializationBuffer bb) { - short sinv = bb.getShort(); - SInvokable invokable = SnapshotBackend.lookupInvokable(sinv); - FrameDescriptor fd = ((Method) invokable.getInvokable()).getLexicalScope().getOuterMethod() - .getMethod().getFrameDescriptor(); - - // read num args - int numArgs = bb.get(); - Object[] args = new Object[numArgs]; - - // read args - for (int i = 0; i < numArgs; i++) { - Object arg = bb.getReference(); - if (DeserializationBuffer.needsFixup(arg)) { - bb.installFixup(new BlockArgumentFixup(args, i)); - } else { - args[i] = arg; + @Override + public Object deserialize(final DeserializationBuffer bb) { + // read num args + int numArgs = bb.get(); + Object[] args = new Object[numArgs]; + + // read args + for (int i = 0; i < numArgs; i++) { + Object arg = bb.getReference(); + if (DeserializationBuffer.needsFixup(arg)) { + bb.installFixup(new BlockArgumentFixup(args, i)); + } else { + args[i] = arg; + } } - } - MaterializedFrame frame = Truffle.getRuntime().createMaterializedFrame(args, fd); + MaterializedFrame frame = + Truffle.getRuntime().createMaterializedFrame(args, frameDescriptor); - int numSlots = bb.get(); - if (numSlots > 0) { - assert numSlots == fd.getSlots().size(); + int numSlots = bb.get(); + if (numSlots > 0) { + assert numSlots == frameDescriptor.getSlots().size(); - for (int i = 0; i < numSlots; i++) { - FrameSlot slot = fd.getSlots().get(i); + for (int i = 0; i < numSlots; i++) { + FrameSlot slot = frameDescriptor.getSlots().get(i); - Object o = bb.getReference(); + Object o = bb.getReference(); - if (DeserializationBuffer.needsFixup(o)) { - bb.installFixup(new FrameSlotFixup(frame, slot)); - } else { + if (DeserializationBuffer.needsFixup(o)) { + bb.installFixup(new FrameSlotFixup(frame, slot)); + } else { + if (slot.getIdentifier() instanceof Internal) { + FrameOnStackMarker fosm = new FrameOnStackMarker(); + if (!(boolean) o) { + fosm.frameNoLongerOnStack(); + } + o = fosm; + } - switch (fd.getFrameSlotKind(slot)) { - case Boolean: + boolean illegal = frameDescriptor.getFrameSlotKind(slot) == FrameSlotKind.Illegal; + + if (o instanceof Boolean) { frame.setBoolean(slot, (boolean) o); - break; - case Double: + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Boolean); + } + } else if (o instanceof Double) { frame.setDouble(slot, (double) o); - break; - case Long: + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Double); + } + } else if (o instanceof Long) { frame.setLong(slot, (long) o); - break; - case Object: - if (slot.getIdentifier() instanceof Internal) { - FrameOnStackMarker fosm = new FrameOnStackMarker(); - if (!(boolean) o) { - fosm.frameNoLongerOnStack(); - } - o = fosm; + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Long); } + } else { frame.setObject(slot, o); - break; - case Illegal: - // uninitialized variable, uses default - frame.setObject(slot, o); - break; - default: - throw new IllegalArgumentException("Unexpected SlotKind"); + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Object); + } + } } } } - } - - return new SBlock(invokable, frame); - } - - public static class BlockArgumentFixup extends FixupInformation { - Object[] args; - int idx; - public BlockArgumentFixup(final Object[] args, final int idx) { - this.args = args; - this.idx = idx; - } - - @Override - public void fixUp(final Object o) { - args[idx] = o; - } - } - - public static class FrameSlotFixup extends FixupInformation { - FrameSlot slot; - MaterializedFrame frame; - - public FrameSlotFixup(final MaterializedFrame frame, final FrameSlot slot) { - this.frame = frame; - this.slot = slot; - } - - @Override - public void fixUp(final Object o) { - frame.setObject(slot, o); + frame.materialize(); + return frame; } } } diff --git a/src/tools/snapshot/nodes/CachedSerializationNode.java b/src/tools/snapshot/nodes/CachedSerializationNode.java index 5e952b81f..9c3eb4d06 100644 --- a/src/tools/snapshot/nodes/CachedSerializationNode.java +++ b/src/tools/snapshot/nodes/CachedSerializationNode.java @@ -1,46 +1,77 @@ package tools.snapshot.nodes; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import som.interpreter.Types; import som.interpreter.nodes.dispatch.DispatchGuard; -import som.interpreter.objectstorage.ObjectTransitionSafepoint; -import som.vmobjects.SObject; +import som.primitives.ObjectPrims.ClassPrim; +import som.primitives.ObjectPrimsFactory.ClassPrimFactory; +import som.vm.VmSettings; +import som.vmobjects.SClass; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; +import tools.snapshot.nodes.ObjectSerializationNodes.SObjectSerializationNode; @GenerateNodeFactory public abstract class CachedSerializationNode extends AbstractSerializationNode { - private final DispatchGuard guard; - private final AbstractSerializationNode cachedSerializer; + public static final int MAX_DEPTH = VmSettings.SNAPSHOT_INLINING_DEPTH; + @Child ClassPrim classprim; - public CachedSerializationNode(final Object o) { - super(null); - this.guard = DispatchGuard.create(o); - this.cachedSerializer = Types.getClassOf(o).getSerializer(); + protected final int depth; + + public CachedSerializationNode(final int depth) { + super(); + this.depth = depth; } - @Specialization - public void serialize(final Object o, final SnapshotBuffer sb) { + protected static DispatchGuard createDispatchGuard(final Object o) { + return DispatchGuard.create(o); + } + + protected static AbstractSerializationNode getSerializer(final Object o, final int depth) { + SClass clazz = Types.getClassOf(o); + NodeFactory factory = clazz.getSerializerFactory(); + + if (factory.getNodeClass() == SObjectSerializationNode.class) { + return factory.createNode(clazz.getInstanceFactory(), depth + 1); + } + + return factory.createNode(); + } + + protected static boolean execGuard(final Object o, final DispatchGuard guard, + final Assumption objectLayoutIsLatest) { try { - if (guard.entryMatches(o)) { - cachedSerializer.execute(o, sb); - } else { - Types.getClassOf(o).serialize(o, sb); - } + return guard.entryMatches(o); } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - SObject so = (SObject) o; - if (!so.isLayoutCurrent()) { - // we have to update the layout to avoid stackoverflow - ObjectTransitionSafepoint.INSTANCE.transitionObject(so); - } - replace(CachedSerializationNodeFactory.create(o)).serialize(o, sb); + return false; // Layout in guard is outdated + } + } + + @Specialization(guards = {"execGuard(o, guard, objectLayoutIsLatest)", "depth < MAX_DEPTH"}, + assumptions = "objectLayoutIsLatest") + public void serialize(final Object o, final SnapshotBuffer sb, + @Cached("createDispatchGuard(o)") final DispatchGuard guard, + @Cached("guard.getAssumption()") final Assumption objectLayoutIsLatest, + @Cached("getSerializer(o, depth)") final AbstractSerializationNode serializer) { + serializer.execute(o, sb); + } + + @Specialization + public void fallback(final Object o, final SnapshotBuffer sb) { + if (classprim == null) { + CompilerDirectives.transferToInterpreter(); + classprim = ClassPrimFactory.create(null); } + + classprim.executeEvaluated(o).serialize(o, sb); } @Override diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 6321d3df8..9e152d3d7 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -1,12 +1,13 @@ package tools.snapshot.nodes; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.ExplodeLoop; import som.interpreter.SomLanguage; -import som.interpreter.Types; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage; import som.interpreter.actors.EventualMessage.DirectMessage; @@ -15,8 +16,9 @@ import som.interpreter.actors.EventualMessage.PromiseSendMessage; import som.interpreter.actors.EventualSendNode; import som.interpreter.actors.SPromise; +import som.interpreter.actors.SPromise.Resolution; import som.interpreter.actors.SPromise.SResolver; -import som.interpreter.objectstorage.ClassFactory; +import som.interpreter.actors.SPromise.STracingPromise; import som.primitives.actors.PromisePrims; import som.vm.constants.Classes; import som.vm.constants.Nil; @@ -25,6 +27,7 @@ import tools.concurrency.TracingActors.TracingActor; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; +import tools.snapshot.SnapshotRecord; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; @@ -32,15 +35,24 @@ @GenerateNodeFactory public abstract class MessageSerializationNode extends AbstractSerializationNode { - public MessageSerializationNode(final ClassFactory factory) { - super(factory); - } + protected static final int COMMONALITY_BYTES = 7; - public MessageSerializationNode() { - super(Classes.messageClass.getInstanceFactory()); - } + private final SSymbol selector; - protected static final int COMMONALITY_BYTES = 7; + @Children private final CachedSerializationNode[] serializationNodes; + + public MessageSerializationNode(final SSymbol selector) { + this.selector = selector; + this.serializationNodes = + new CachedSerializationNode[selector.getNumberOfSignatureArguments()]; + + assert serializationNodes.length < 32 : "We assume the number of args is reasonable, but was huge: " + + serializationNodes.length; + + for (int i = 0; i < serializationNodes.length; i++) { + serializationNodes[i] = CachedSerializationNodeFactory.create(0); + } + } public enum MessageType { DirectMessage, CallbackMessage, PromiseMessage, UndeliveredPromiseMessage, DirectMessageNR, @@ -70,29 +82,38 @@ public static MessageType getMessageType(final byte ordinal) { // Do we want to serialize messages with other object and just keep their addresses ready, // or do we want to put them into a separate buffer performance wise there shoudn't be much // of a difference - - // TODO possibly explode as optimization, use cached serialization nodes for the args... + @ExplodeLoop protected final void doArguments(final Object[] args, final int base, final SnapshotBuffer sb) { + CompilerAsserts.partialEvaluationConstant(serializationNodes.length); + CompilerAsserts.compilationConstant(serializationNodes.length); - // assume number of args is reasonable - assert args.length < 2 * Byte.MAX_VALUE; - if (args.length > 0) { - // special case for callback message - sb.putByteAt(base, (byte) args.length); - for (int i = 0; i < args.length; i++) { - if (args[i] == null) { - if (!sb.getRecord().containsObject(Nil.nilObject)) { - Classes.nilClass.serialize(Nil.nilObject, sb); - } - sb.putLongAt((base + 1) + i * Long.BYTES, - sb.getRecord().getObjectPointer(Nil.nilObject)); - } else { - if (!sb.getRecord().containsObject(args[i])) { - Types.getClassOf(args[i]).serialize(args[i], sb); - } - sb.putLongAt((base + 1) + i * Long.BYTES, sb.getRecord().getObjectPointer(args[i])); + assert serializationNodes.length == args.length; + + if (serializationNodes.length <= 0) { + return; + } + + // special case for callback message + sb.putByteAt(base, (byte) serializationNodes.length); + + for (int i = 0; i < serializationNodes.length; i++) { + final Object obj = args[i]; + + SnapshotRecord record = sb.getRecord(); + if (obj == null) { + if (!record.containsObjectUnsync(Nil.nilObject)) { + Classes.nilClass.serialize(Nil.nilObject, sb); } + sb.putLongAt((base + 1) + i * Long.BYTES, record.getObjectPointer(Nil.nilObject)); + } else if (obj instanceof SPromise) { + PromiseSerializationNodes.handleReferencedPromise((SPromise) obj, sb, + (base + 1) + i * Long.BYTES); + } else { + if (!record.containsObjectUnsync(obj)) { + serializationNodes[i].execute(obj, sb); + } + sb.putLongAt((base + 1) + i * Long.BYTES, record.getObjectPointer(obj)); } } } @@ -113,38 +134,39 @@ protected long doDirectMessage(final DirectMessage dm, final SnapshotBuffer sb) SResolver resolver = dm.getResolver(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.DirectMessage, dm.getSelector(), (TracingActor) dm.getSender(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.DirectMessage, selector, (TracingActor) dm.getSender(), base, sb); serializeResolver(resolver, sb); sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(resolver)); base += COMMONALITY_BYTES + Long.BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization protected long doDirectMessageNoResolver(final DirectMessage dm, final SnapshotBuffer sb) { Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.DirectMessageNR, dm.getSelector(), - (TracingActor) dm.getSender(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.DirectMessageNR, selector, (TracingActor) dm.getSender(), base, sb); base += COMMONALITY_BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = "dm.getResolver() != null") @@ -153,22 +175,23 @@ protected long doCallbackMessage(final PromiseCallbackMessage dm, final Snapshot SPromise prom = dm.getPromise(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 + + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.CallbackMessage, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.CallbackMessage, selector, (TracingActor) dm.getSender(), base, sb); - serializePromise(prom, sb); serializeResolver(resolver, sb); sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(resolver)); - sb.putLongAt(base + COMMONALITY_BYTES + Long.BYTES, sb.getRecord().getObjectPointer(prom)); + PromiseSerializationNodes.handleReferencedPromise(prom, sb, + base + COMMONALITY_BYTES + Long.BYTES); base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization @@ -177,17 +200,24 @@ protected long doCallbackMessageNoResolver(final PromiseCallbackMessage dm, SPromise prom = dm.getPromise(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.CallbackMessageNR, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.CallbackMessageNR, selector, (TracingActor) dm.getSender(), base, sb); - serializePromise(prom, sb); - sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(prom)); + PromiseSerializationNodes.handleReferencedPromise(prom, sb, base + COMMONALITY_BYTES); base += COMMONALITY_BYTES + Long.BYTES; + return processArguments(sb, args, base, start); + } + + private long processArguments(final SnapshotBuffer sb, final Object[] args, final int base, + final long start) { doArguments(args, base, sb); return sb.calculateReference(start); @@ -198,31 +228,29 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe SResolver resolver = dm.getResolver(); SPromise prom = dm.getPromise(); - TracingActor fsender = (TracingActor) dm.getFinalSender(); + int fsender = ((STracingPromise) prom).getResolvingActor(); Object[] args = dm.getArgs(); - Object receiver = args[0]; - args[0] = dm.getPromise(); int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES + 1 - + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.PromiseMessage, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.PromiseMessage, selector, (TracingActor) dm.getSender(), base, sb); - serializePromise(prom, sb); serializeResolver(resolver, sb); sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(resolver)); - sb.putLongAt(base + COMMONALITY_BYTES + Long.BYTES, sb.getRecord().getObjectPointer(prom)); - sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES + Long.BYTES, fsender.getActorId()); - base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES; + PromiseSerializationNodes.handleReferencedPromise(prom, sb, + base + COMMONALITY_BYTES + Long.BYTES); - doArguments(args, base, sb); - args[0] = receiver; + sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES + Long.BYTES, fsender); + base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES; - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = "dm.isDelivered()") @@ -230,26 +258,24 @@ protected long doPromiseMessageNoResolver(final PromiseSendMessage dm, final SnapshotBuffer sb) { SPromise prom = dm.getPromise(); - TracingActor fsender = (TracingActor) dm.getFinalSender(); + int fsender = ((STracingPromise) prom).getResolvingActor(); Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Integer.BYTES + 1 - + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.PromiseMessageNR, dm.getSelector(), - (TracingActor) dm.getSender(), base, sb); + assert dm.getSelector() == selector; - serializePromise(prom, sb); + doCommonalities(MessageType.PromiseMessageNR, selector, + (TracingActor) dm.getSender(), base, sb); - sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(prom)); - sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES, fsender.getActorId()); + PromiseSerializationNodes.handleReferencedPromise(prom, sb, base + COMMONALITY_BYTES); + sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES, fsender); base += COMMONALITY_BYTES + Long.BYTES + Integer.BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = {"!dm.isDelivered()", "dm.getResolver() != null"}) @@ -259,20 +285,23 @@ protected long doUndeliveredPromiseMessage(final PromiseSendMessage dm, SResolver resolver = dm.getResolver(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 + + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; serializeResolver(resolver, sb); - doCommonalities(MessageType.UndeliveredPromiseMessage, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.UndeliveredPromiseMessage, selector, (TracingActor) dm.getSender(), base, sb); sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(resolver)); - base += COMMONALITY_BYTES + Long.BYTES; - - doArguments(args, base, sb); + PromiseSerializationNodes.handleReferencedPromise(dm.getPromise(), sb, + base + COMMONALITY_BYTES + Long.BYTES); + base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES; - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = "!dm.isDelivered()") @@ -280,22 +309,20 @@ protected long doUndeliveredPromiseMessageNoResolver(final PromiseSendMessage dm final SnapshotBuffer sb) { Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.UndeliveredPromiseMessageNR, dm.getSelector(), - (TracingActor) dm.getSender(), base, sb); - base += COMMONALITY_BYTES; - - doArguments(args, base, sb); + assert dm.getSelector() == selector; - return sb.calculateReference(start); - } + doCommonalities(MessageType.UndeliveredPromiseMessageNR, selector, + (TracingActor) dm.getSender(), base, sb); + PromiseSerializationNodes.handleReferencedPromise(dm.getPromise(), sb, + base + COMMONALITY_BYTES); + base += COMMONALITY_BYTES + Long.BYTES; - @TruffleBoundary - private void serializePromise(final SPromise prom, final SnapshotBuffer sb) { - SPromise.getPromiseClass().serialize(prom, sb); + return processArguments(sb, args, base, start); } @TruffleBoundary @@ -304,11 +331,16 @@ private void serializeResolver(final SResolver resolver, final SnapshotBuffer sb } @Override - public EventualMessage deserialize(final DeserializationBuffer bb) { + protected Object deserialize(final DeserializationBuffer bb) { + return deserializeMessage(bb); + } + + public static EventualMessage deserializeMessage(final DeserializationBuffer bb) { // commonalities MessageType type = MessageType.getMessageType(bb.get()); SSymbol selector = SnapshotBackend.getSymbolForId(bb.getShort()); Actor sender = SnapshotBackend.lookupActor(bb.getInt()); + assert sender != null; switch (type) { case CallbackMessage: @@ -332,7 +364,7 @@ public EventualMessage deserialize(final DeserializationBuffer bb) { } } - private PromiseCallbackMessage deserializeCallback(final Actor sender, + private static PromiseCallbackMessage deserializeCallback(final Actor sender, final DeserializationBuffer bb, final SResolver resolver) { PromiseMessageFixup pmf = null; @@ -345,8 +377,9 @@ private PromiseCallbackMessage deserializeCallback(final Actor sender, } else { prom = (SPromise) promObj; } - Object[] args = parseArguments(bb); + Object[] args = parseArguments(bb, false); + assert args[0] != null; RootCallTarget onReceive = PromisePrims.createReceived((SBlock) args[0]); PromiseCallbackMessage pcm = new PromiseCallbackMessage(sender, (SBlock) args[0], resolver, @@ -361,21 +394,23 @@ private PromiseCallbackMessage deserializeCallback(final Actor sender, return pcm; } - private DirectMessage deserializeDirect(final SSymbol selector, final Actor sender, + private static DirectMessage deserializeDirect(final SSymbol selector, final Actor sender, final DeserializationBuffer bb, final SResolver resolver) { - Object[] args = parseArguments(bb); - RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, null, - SomLanguage.getLanguage(getRootNode())); + Object[] args = parseArguments(bb, false); + RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, + SomLanguage.getSyntheticSource("Deserialized Message", "Trace").createSection(1), + SomLanguage.getCurrent()); DirectMessage dm = - new DirectMessage(EventualMessage.getActorCurrentMessageIsExecutionOn(), selector, + new DirectMessage(SnapshotBackend.getCurrentActor(), selector, args, sender, resolver, onReceive, false, false); return dm; } - private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Actor sender, + private static PromiseSendMessage deserializeDelivered(final SSymbol selector, + final Actor sender, final DeserializationBuffer bb, final SResolver resolver) { Object promObj = bb.getReference(); PromiseMessageFixup pmf = null; @@ -388,18 +423,24 @@ private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Ac prom = (SPromise) promObj; } - Actor finalSender = SnapshotBackend.lookupActor(bb.getInt()); - Object[] args = parseArguments(bb); - RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, null, - SomLanguage.getLanguage(getRootNode())); + TracingActor finalSender = (TracingActor) SnapshotBackend.lookupActor(bb.getInt()); + Object[] args = parseArguments(bb, false); + RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, + SomLanguage.getSyntheticSource("Deserialized Message", "Trace").createSection(1), + SomLanguage.getCurrent()); // backup value for resolution. Object value = args[0]; - args[0] = prom; - if (!(args[0] instanceof SPromise)) { - // expects args[0] to be a promise + // constructor expects args[0] to be a promise + if (prom == null) { args[0] = SPromise.createPromise(sender, false, false, null); + } else { + args[0] = prom; + if (!prom.isCompleted()) { + prom.resolveFromSnapshot(value, Resolution.SUCCESSFUL, finalSender, true); + ((STracingPromise) prom).setResolvingActorForSnapshot(finalSender.getActorId()); + } } PromiseSendMessage psm = @@ -408,26 +449,50 @@ private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Ac if (pmf != null) { pmf.setMessage(psm); } - psm.resolve(value, EventualMessage.getActorCurrentMessageIsExecutionOn(), + psm.resolve(value, SnapshotBackend.getCurrentActor(), finalSender); return psm; } - private PromiseSendMessage deserializeUndelivered(final SSymbol selector, final Actor sender, + private static PromiseSendMessage deserializeUndelivered(final SSymbol selector, + final Actor sender, final DeserializationBuffer bb, final SResolver resolver) { - Object[] args = parseArguments(bb); + Object prom = bb.getReference(); + PromiseMessageFixup pmf = null; + PromiseMessageFixup argf = null; + if (DeserializationBuffer.needsFixup(prom)) { + pmf = new PromiseMessageFixup(); + bb.installFixup(pmf); + } + + Object[] args = parseArguments(bb, true); RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, SomLanguage.getSyntheticSource("Deserialized Message", "Trace").createSection(1), - SomLanguage.getLanguage(this.getRootNode())); + SomLanguage.getCurrent()); - if (!(args[0] instanceof SPromise)) { + if ((args[0] instanceof PromiseMessageFixup)) { + argf = (PromiseMessageFixup) args[0]; // expects args[0] to be a promise args[0] = SPromise.createPromise(sender, false, false, null); + } else if (!(args[0] instanceof SPromise)) { + if (pmf != null) { + args[0] = SPromise.createPromise(sender, false, false, null); + } else { + args[0] = prom; + } } PromiseSendMessage psm = new PromiseSendMessage(selector, args, sender, resolver, onReceive, false, false); + + if (pmf != null) { + pmf.setMessage(psm); + } + if (argf != null) { + argf.setMessage(psm); + } + return psm; } @@ -437,13 +502,19 @@ private PromiseSendMessage deserializeUndelivered(final SSymbol selector, final * * @return An array containing the references to the deserialized objects. */ - private Object[] parseArguments(final DeserializationBuffer bb) { + private static Object[] parseArguments(final DeserializationBuffer bb, + final boolean undelivered) { int argCnt = bb.get(); Object[] args = new Object[argCnt]; for (int i = 0; i < argCnt; i++) { Object arg = bb.getReference(); if (DeserializationBuffer.needsFixup(arg)) { bb.installFixup(new MessageArgumentFixup(args, i)); + if (i == 0 && undelivered) { + PromiseMessageFixup pmf = new PromiseMessageFixup(); + bb.installFixup(pmf); + args[0] = pmf; + } } else { args[i] = arg; } @@ -475,7 +546,7 @@ public void setMessage(final PromiseMessage pm) { @Override public void fixUp(final Object o) { - assert pm != null; + assert pm != null && o != null; this.pm.setPromise((SPromise) o); } } diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index eea8f33d0..b24afa98f 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.ExplodeLoop; @@ -20,37 +21,63 @@ import som.interpreter.objectstorage.ObjectLayout; import som.interpreter.objectstorage.ObjectTransitionSafepoint; import som.interpreter.objectstorage.StorageLocation; +import som.vmobjects.SClass; import som.vmobjects.SObject; import som.vmobjects.SObject.SImmutableObject; import som.vmobjects.SObject.SMutableObject; -import som.vmobjects.SObjectWithClass; import som.vmobjects.SObjectWithClass.SObjectWithoutFields; -import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; +import tools.snapshot.SnapshotRecord; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; import tools.snapshot.nodes.ObjectSerializationNodesFactory.SObjectSerializationNodeFactory; import tools.snapshot.nodes.ObjectSerializationNodesFactory.SObjectWithoutFieldsSerializationNodeFactory; -import tools.snapshot.nodes.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory; public abstract class ObjectSerializationNodes { + public static final int FIELD_SIZE = 8; + public static final int MAX_FIELD_CNT = Byte.MAX_VALUE; public abstract static class ObjectSerializationNode extends AbstractSerializationNode { - protected class SlotDefinitionSorter implements Comparator { + protected static class SlotDefinitionSorter implements Comparator { @Override public int compare(final SlotDefinition o1, final SlotDefinition o2) { return o1.getName().getString().compareTo(o2.getName().getString()); } } - protected ObjectSerializationNode(final ClassFactory classFact) { - super(classFact); + protected final ClassFactory classFact; + + protected final int depth; + + protected ObjectSerializationNode(final ClassFactory instanceFactory) { + this.classFact = instanceFactory; + this.depth = 0; } - public static ObjectSerializationNode create(final ClassFactory classFact) { - return UninitializedObjectSerializationNodeFactory.create(classFact); + protected ObjectSerializationNode(final ClassFactory instanceFactory, final int depth) { + this.classFact = instanceFactory; + this.depth = depth; + } + + public static AbstractSerializationNode create(final ClassFactory instanceFactory, + final int depth) { + if (instanceFactory.hasSlots()) { + return SObjectSerializationNodeFactory.create(instanceFactory, + createReadNodes(instanceFactory), depth); + } else { + return SObjectWithoutFieldsSerializationNodeFactory.create(); + } + } + + public static NodeFactory getNodeFactory( + final ClassFactory instanceFactory) { + if (instanceFactory.hasSlots()) { + return SObjectSerializationNodeFactory.getInstance(); + } else { + return SObjectWithoutFieldsSerializationNodeFactory.getInstance(); + } } protected final CachedSlotWrite[] createWriteNodes(final SObject o) { @@ -82,11 +109,11 @@ protected final CachedSlotWrite[] createWriteNodes(final SObject o) { return writes; } - protected final CachedSlotRead[] createReadNodes(final SObject o) { + protected static final CachedSlotRead[] createReadNodes(final ClassFactory factory) { CompilerDirectives.transferToInterpreter(); ArrayList definitions = new ArrayList<>(); - ObjectLayout layout = classFact.getInstanceLayout(); + ObjectLayout layout = factory.getInstanceLayout(); for (SlotDefinition sd : layout.getStorageLocations().getKeys()) { definitions.add(sd); } @@ -102,51 +129,16 @@ protected final CachedSlotRead[] createReadNodes(final SObject o) { AbstractDispatchNode next = UninitializedDispatchNode.createLexicallyBound(loc.getSlot().getSourceSection(), - loc.getSlot().getName(), classFact.getMixinDefinition().getMixinId()); + loc.getSlot().getName(), factory.getMixinDefinition().getMixinId()); - reads[i] = - loc.getReadNode(SlotAccess.FIELD_READ, - DispatchGuard.createSObjectCheck(o), - next, - false); + reads[i] = loc.getReadNode( + SlotAccess.FIELD_READ, DispatchGuard.createSObjectCheck(factory), next, false); } return reads; } } - @GenerateNodeFactory - public abstract static class UninitializedObjectSerializationNode - extends ObjectSerializationNode { - - protected UninitializedObjectSerializationNode(final ClassFactory classFact) { - super(classFact); - } - - @Specialization - public void serialize(final SObjectWithClass o, final SnapshotBuffer sb) { - if (o instanceof SObject) { - replace(SObjectSerializationNodeFactory.create(classFact, - createReadNodes((SObject) o))).serialize((SObject) o, sb); - } else if (o instanceof SObjectWithoutFields) { - replace(SObjectWithoutFieldsSerializationNodeFactory.create(classFact)).serialize( - (SObjectWithoutFields) o, - sb); - } - } - - @Override - public Object deserialize(final DeserializationBuffer sb) { - if (classFact.hasSlots()) { - return replace(SObjectSerializationNodeFactory.create(classFact, null)).deserialize( - sb); - } else { - return replace( - SObjectWithoutFieldsSerializationNodeFactory.create(classFact)).deserialize(sb); - } - } - } - // These Nodes may need to be replaced in the class when object layouts change @GenerateNodeFactory @@ -159,73 +151,79 @@ public abstract static class SObjectSerializationNode @Children private CachedSerializationNode[] cachedSerializers; protected final ObjectLayout layout; - protected SObjectSerializationNode(final ClassFactory classFact, - final CachedSlotRead[] reads) { - super(classFact); + protected SObjectSerializationNode(final ClassFactory instanceFactory, + final CachedSlotRead[] reads, final int depth) { + super(instanceFactory); layout = classFact.getInstanceLayout(); fieldReads = insert(reads); fieldCnt = fieldReads.length; + + cachedSerializers = new CachedSerializationNode[fieldCnt]; + for (int i = 0; i < fieldCnt; i++) { + cachedSerializers[i] = CachedSerializationNodeFactory.create(depth); + } + } + + protected SObjectSerializationNode(final ClassFactory instanceFactory, final int depth) { + this(instanceFactory, createReadNodes(instanceFactory), depth); } @Specialization public void serialize(final SObject so, final SnapshotBuffer sb) { if (!so.isLayoutCurrent()) { + CompilerDirectives.transferToInterpreter(); ObjectTransitionSafepoint.INSTANCE.transitionObject(so); } if (!layout.isValid()) { // replace this with a new node for the new layout SObjectSerializationNode replacement = - SObjectSerializationNodeFactory.create(classFact, createReadNodes(so)); + SObjectSerializationNodeFactory.create(classFact, + createReadNodes(so.getFactory()), depth); replace(replacement).serialize(so, sb); } else { doCached(so, sb); } } - protected final CachedSerializationNode[] getSerializers(final SObject o) { - CachedSerializationNode[] nodes = new CachedSerializationNode[fieldCnt]; - for (int i = 0; i < fieldCnt; i++) { - Object value = fieldReads[i].read(o); - nodes[i] = CachedSerializationNodeFactory.create(value); - } - return nodes; - } - @ExplodeLoop public void doCached(final SObject o, final SnapshotBuffer sb) { - int base = sb.addObjectWithFields(o, classFact, fieldCnt); + int base = sb.addObject(o, o.getSOMClass(), FIELD_SIZE * fieldCnt); - if (cachedSerializers == null) { - cachedSerializers = insert(getSerializers(o)); - } + assert fieldCnt < MAX_FIELD_CNT; + SnapshotRecord record = sb.getRecord(); for (int i = 0; i < fieldCnt; i++) { Object value = fieldReads[i].read(o); // TODO type profiles could be an optimization (separate profile for each slot) // TODO optimize, maybe it is better to add an integer to the objects (indicating their // offset) rather than using a map. - if (!sb.getRecord().containsObject(value)) { + if (!record.containsObjectUnsync(value)) { // Referenced Object not yet in snapshot - cachedSerializers[i].serialize(value, sb); + cachedSerializers[i].execute(value, sb); } - sb.putLongAt(base + (8 * i), sb.getRecord().getObjectPointer(value)); + sb.putLongAt(base + (8 * i), record.getObjectPointer(value)); } } @Override public Object deserialize(final DeserializationBuffer sb) { + throw new UnsupportedOperationException(); + } + + @Override + public Object deserialize(final DeserializationBuffer sb, final SClass clazz) { SObject o; if (classFact.hasOnlyImmutableFields()) { o = new SImmutableObject( - SnapshotBackend.lookupClass(classFact.getIdentifier()), + clazz, classFact, classFact.getInstanceLayout()); } else { - o = new SMutableObject(SnapshotBackend.lookupClass(classFact.getIdentifier()), + o = new SMutableObject(clazz, classFact, classFact.getInstanceLayout()); } @@ -234,23 +232,20 @@ public Object deserialize(final DeserializationBuffer sb) { fieldWrites = insert(createWriteNodes(o)); } + sb.putObject(o); + for (int i = 0; i < fieldWrites.length; i++) { - Object ref = sb.getReference(); - if (DeserializationBuffer.needsFixup(ref)) { - sb.installFixup(new SlotFixup(o, fieldWrites[i])); - } else { - fieldWrites[i].doWrite(o, ref); - } + sb.installObjectFixup(o, fieldWrites[i]); } return o; } - private static class SlotFixup extends FixupInformation { + public static class SlotFixup extends FixupInformation { final CachedSlotWrite csw; final SObject obj; - SlotFixup(final SObject obj, final CachedSlotWrite csw) { + public SlotFixup(final SObject obj, final CachedSlotWrite csw) { this.csw = csw; this.obj = obj; } @@ -264,22 +259,22 @@ public void fixUp(final Object res) { @GenerateNodeFactory public abstract static class SObjectWithoutFieldsSerializationNode - extends ObjectSerializationNode { - - protected SObjectWithoutFieldsSerializationNode(final ClassFactory classFact) { - super(classFact); - } + extends AbstractSerializationNode { @ExplodeLoop @Specialization public void serialize(final SObjectWithoutFields o, final SnapshotBuffer sb) { - sb.addObject(o, classFact, 0); + sb.addObject(o, o.getSOMClass(), 0); } @Override public Object deserialize(final DeserializationBuffer sb) { - return new SObjectWithoutFields(SnapshotBackend.lookupClass(classFact.getIdentifier()), - classFact); + throw new UnsupportedOperationException(); + } + + @Override + public Object deserialize(final DeserializationBuffer sb, final SClass clazz) { + return new SObjectWithoutFields(clazz, clazz.getInstanceFactory()); } } } diff --git a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java index 718e1acb5..468136791 100644 --- a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java +++ b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java @@ -4,40 +4,39 @@ import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; -import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import som.interpreter.SomLanguage; import som.interpreter.actors.SFarReference; -import som.interpreter.objectstorage.ClassFactory; import som.vm.constants.Classes; import som.vm.constants.Nil; 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.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; +import tools.snapshot.deserialization.SnapshotParser; public abstract class PrimitiveSerializationNodes { @GenerateNodeFactory public abstract static class StringSerializationNode extends AbstractSerializationNode { - public StringSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof String; String s = (String) o; byte[] data = s.getBytes(StandardCharsets.UTF_8); - int base = sb.addObject(o, classFact, data.length + 4); + int base = sb.addObject(o, Classes.stringClass, data.length + 4); sb.putIntAt(base, data.length); sb.putBytesAt(base + 4, data); } @@ -55,15 +54,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class IntegerSerializationNode extends AbstractSerializationNode { - public IntegerSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof Long; long l = (long) o; - int base = sb.addObject(o, classFact, Long.BYTES); + int base = sb.addObject(o, Classes.integerClass, Long.BYTES); sb.putLongAt(base, l); } @@ -76,15 +71,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class DoubleSerializationNode extends AbstractSerializationNode { - public DoubleSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof Double; double d = (double) o; - int base = sb.addObject(o, classFact, Double.BYTES); + int base = sb.addObject(o, Classes.doubleClass, Double.BYTES); sb.putDoubleAt(base, d); } @@ -97,15 +88,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class BooleanSerializationNode extends AbstractSerializationNode { - public BooleanSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof Boolean; boolean b = (boolean) o; - int base = sb.addObject(o, classFact, 1); + int base = sb.addObject(o, Classes.booleanClass, 1); sb.putByteAt(base, (byte) (b ? 1 : 0)); } @@ -118,15 +105,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class TrueSerializationNode extends AbstractSerializationNode { - public TrueSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof Boolean; assert ((boolean) o); - sb.addObject(o, classFact, 0); + sb.addObject(o, Classes.trueClass, 0); } @Override @@ -138,15 +121,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class FalseSerializationNode extends AbstractSerializationNode { - public FalseSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof Boolean; assert !((boolean) o); - sb.addObject(o, classFact, 0); + sb.addObject(o, Classes.falseClass, 0); } @Override @@ -158,15 +137,11 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class SymbolSerializationNode extends AbstractSerializationNode { - public SymbolSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof SSymbol; SSymbol ss = (SSymbol) o; - int base = sb.addObject(o, classFact, 2); + int base = sb.addObject(o, Classes.symbolClass, 2); sb.putShortAt(base, ss.getSymbolId()); } @@ -180,41 +155,62 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class ClassSerializationNode extends AbstractSerializationNode { - public ClassSerializationNode(final ClassFactory classFact) { - super(classFact); + @Specialization(guards = "cls.isValue()") + protected void doValueClass(final SClass cls, final SnapshotBuffer sb) { + int base = sb.addObject(cls, Classes.classClass, Integer.BYTES + Long.BYTES); + sb.putIntAt(base, cls.getIdentity()); + SObjectWithClass outer = cls.getEnclosingObject(); + outer.getSOMClass().serialize(outer, sb); + sb.putLongAt(base + Integer.BYTES, sb.getRecord().getObjectPointer(outer)); + + SnapshotBackend.registerClassLocation(cls.getIdentity(), + sb.getRecord().getObjectPointer(cls)); } - protected short getSymbolId(final SClass clazz) { - return clazz.getMixinDefinition().getIdentifier().getSymbolId(); + protected TracingActor getMain() { + CompilerDirectives.transferToInterpreter(); + return (TracingActor) SomLanguage.getCurrent().getVM().getMainActor(); } - @Specialization - protected void doCached(final SClass cls, final SnapshotBuffer sb, - @Cached("getSymbolId(cls)") final short cachedId) { - CompilerAsserts.compilationConstant(cachedId); - int base = sb.addObject(cls, Classes.classClass.getFactory(), 2); - sb.putShortAt(base, cachedId); + @Specialization(guards = "!cls.isValue()") + protected void doNotValueClass(final SClass cls, final SnapshotBuffer sb, + @Cached("getMain()") final TracingActor main) { + int base = sb.addObject(cls, Classes.classClass, Integer.BYTES + Long.BYTES); + sb.putIntAt(base, cls.getIdentity()); + + SObjectWithClass outer = cls.getEnclosingObject(); + assert outer != null; + TracingActor owner = cls.getOwnerOfOuter(); + if (owner == null) { + owner = main; + } + owner.getSnapshotRecord().farReference(outer, sb, base + Integer.BYTES); + + SnapshotBackend.registerClassLocation(cls.getIdentity(), + sb.getRecord().getObjectPointer(cls)); } @Override public Object deserialize(final DeserializationBuffer sb) { - short id = sb.getShort(); - return SnapshotBackend.lookupClass(id); + int id = sb.getInt(); + // SObjectWithClass outer = (SObjectWithClass) sb.getReference(); + return SnapshotBackend.lookupClass(id, sb.position()); + } + + public static long readOuterLocation(final DeserializationBuffer sb) { + sb.getInt(); + return sb.getLong(); } } @GenerateNodeFactory public abstract static class SInvokableSerializationNode extends AbstractSerializationNode { - public SInvokableSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { assert o instanceof SInvokable; SInvokable si = (SInvokable) o; - int base = sb.addObject(si, classFact, Short.BYTES); + int base = sb.addObject(si, Classes.methodClass, Short.BYTES); sb.putShortAt(base, si.getIdentifier().getSymbolId()); } @@ -229,13 +225,9 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class NilSerializationNode extends AbstractSerializationNode { - public NilSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { - sb.addObject(o, classFact, 0); + sb.addObject(o, Classes.nilClass, 0); } @Override @@ -247,13 +239,9 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class FarRefSerializationNode extends AbstractSerializationNode { - public FarRefSerializationNode(final ClassFactory classFact) { - super(classFact); - } - @Specialization public void serialize(final SFarReference o, final SnapshotBuffer sb) { - int base = sb.addObject(o, classFact, Integer.BYTES + Long.BYTES); + int base = sb.addObject(o, SFarReference.getFarRefClass(), Integer.BYTES + Long.BYTES); TracingActor other = (TracingActor) o.getActor(); sb.putIntAt(base, other.getActorId()); @@ -265,14 +253,22 @@ public void serialize(final SFarReference o, final SnapshotBuffer sb) { @Override public Object deserialize(final DeserializationBuffer sb) { - TracingActor other = (TracingActor) SnapshotBackend.lookupActor(sb.getInt()); - DeserializationBuffer otherDB = other.getDeserializationBuffer(); + int actorId = sb.getInt(); + TracingActor other = (TracingActor) SnapshotBackend.lookupActor(actorId); + if (other == null) { + // no messages recorded for this actor, need to create it here. + other = ReplayActor.getActorWithId(actorId); + } + + TracingActor current = SnapshotBackend.getCurrentActor(); + SnapshotParser.setCurrentActor((ReplayActor) other); + Object value = sb.getReference(); + SnapshotParser.setCurrentActor((ReplayActor) current); - Object value = otherDB.getReference(); SFarReference result = new SFarReference(other, value); if (DeserializationBuffer.needsFixup(value)) { - otherDB.installFixup(new FarRefFixupInformation(result)); + sb.installFixup(new FarRefFixupInformation(result)); } return result; diff --git a/src/tools/snapshot/nodes/PromiseSerializationNodes.java b/src/tools/snapshot/nodes/PromiseSerializationNodes.java index d2bee3a72..e87ab36f6 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -2,56 +2,95 @@ import java.util.ArrayList; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; import som.interpreter.Types; -import som.interpreter.actors.EventualMessage; +import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.Resolution; import som.interpreter.actors.SPromise.SResolver; -import som.interpreter.objectstorage.ClassFactory; +import som.interpreter.actors.SPromise.STracingPromise; import tools.concurrency.TracingActors.TracingActor; +import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; public abstract class PromiseSerializationNodes { + private static final byte UNRESOLVED = 0; + private static final byte CHAINED = 1; + private static final byte SUCCESS = 2; + private static final byte ERROR = 3; + + static void handleReferencedPromise(final SPromise prom, + final SnapshotBuffer sb, final int location) { + if (prom.getOwner() == sb.getOwner().getCurrentActor()) { + if (!sb.getRecord().containsObjectUnsync(prom)) { + SPromise.getPromiseClass().serialize(prom, sb); + } + sb.putLongAt(location, sb.getRecord().getObjectPointer(prom)); + } else { + // The Promise belong to another Actor + TracingActor ta = (TracingActor) prom.getOwner(); + if (!ta.getSnapshotRecord().containsObject(ta)) { + ta.getSnapshotRecord().farReference(prom, sb, location); + } else { + sb.putLongAt(location, ta.getSnapshotRecord().getObjectPointer(prom)); + } + } + } @GenerateNodeFactory public abstract static class PromiseSerializationNode extends AbstractSerializationNode { - public PromiseSerializationNode(final ClassFactory classFact) { - super(classFact); - } + @Specialization(guards = "!prom.isCompleted()") + public void doUnresolved(final SPromise prom, final SnapshotBuffer sb) { + int ncp; + int nwr; + int noe; + PromiseMessage whenRes; + PromiseMessage onError; + ArrayList whenResExt; + ArrayList onErrorExt; + SPromise chainedProm; + ArrayList chainedPromExt; + synchronized (prom) { + chainedProm = prom.getChainedPromiseUnsync(); + chainedPromExt = prom.getChainedPromiseExtUnsync(); + ncp = getObjectCnt(chainedProm, chainedPromExt); - @Specialization(guards = "prom.isCompleted()") - public void doResolved(final SPromise prom, final SnapshotBuffer sb) { - int base = sb.addObject(prom, classFact, 1 + Long.BYTES); + whenRes = prom.getWhenResolvedUnsync(); + whenResExt = prom.getWhenResolvedExtUnsync(); + nwr = getObjectCnt(whenRes, whenResExt); - // resolutionstate - switch (prom.getResolutionStateUnsync()) { - case SUCCESSFUL: - sb.putByteAt(base, (byte) 1); - break; - case ERRONEOUS: - sb.putByteAt(base, (byte) 2); - break; - default: - throw new IllegalArgumentException("This shoud be unreachable"); + onError = prom.getOnError(); + onErrorExt = prom.getOnErrorExtUnsync(); + noe = getObjectCnt(onError, onErrorExt); } + int base = + sb.addObject(prom, SPromise.getPromiseClass(), + 1 + Integer.BYTES + 6 + Long.BYTES * (noe + nwr + ncp)); - Object value = prom.getValueForSnapshot(); - if (!sb.getRecord().containsObject(value)) { - Types.getClassOf(value).serialize(value, sb); + // resolutionstate + if (prom.getResolutionStateUnsync() == Resolution.CHAINED) { + sb.putByteAt(base, CHAINED); + } else { + sb.putByteAt(base, UNRESOLVED); } - sb.putLongAt(base + 1, sb.getRecord().getObjectPointer(value)); + + sb.putIntAt(base + 1, ((TracingActor) prom.getOwner()).getActorId()); + base += 1 + Integer.BYTES; + base = serializeMessages(base, nwr, whenRes, whenResExt, sb); + base = serializeMessages(base, noe, onError, onErrorExt, sb); + serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); } - @Specialization(guards = "!prom.isCompleted()") - public void doUnresolved(final SPromise prom, final SnapshotBuffer sb) { + @Specialization(guards = "prom.isCompleted()") + public void doResolved(final SPromise prom, final SnapshotBuffer sb) { int ncp; int nwr; int noe; @@ -74,13 +113,32 @@ public void doUnresolved(final SPromise prom, final SnapshotBuffer sb) { onErrorExt = prom.getOnErrorExtUnsync(); noe = getObjectCnt(onError, onErrorExt); } - int base = sb.addObject(prom, classFact, 1 + 6 + Long.BYTES * (noe + nwr + ncp)); + int base = sb.addObject(prom, SPromise.getPromiseClass(), + 1 + 6 + Integer.BYTES + Integer.BYTES + Long.BYTES * (noe + nwr + ncp + 1)); // resolutionstate - sb.putByteAt(base, (byte) 0); - base++; - base = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); - base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); + switch (prom.getResolutionStateUnsync()) { + case SUCCESSFUL: + sb.putByteAt(base, SUCCESS); + break; + case ERRONEOUS: + sb.putByteAt(base, ERROR); + break; + default: + throw new IllegalArgumentException("This shoud be unreachable"); + } + + Object value = prom.getValueForSnapshot(); + if (!sb.getRecord().containsObjectUnsync(value)) { + Types.getClassOf(value).serialize(value, sb); + } + sb.putIntAt(base + 1, ((TracingActor) prom.getOwner()).getActorId()); + sb.putLongAt(base + 1 + Integer.BYTES, sb.getRecord().getObjectPointer(value)); + sb.putIntAt(base + 1 + +Integer.BYTES + Long.BYTES, + ((STracingPromise) prom).getResolvingActor()); + base += (1 + Integer.BYTES + Integer.BYTES + Long.BYTES); + base = serializeMessages(base, nwr, whenRes, whenResExt, sb); + base = serializeMessages(base, noe, onError, onErrorExt, sb); serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); } @@ -94,19 +152,30 @@ private int getObjectCnt(final Object obj, final ArrayList ext } } - private int serializeWhenResolvedMsgs(final int start, final int cnt, + private int serializeMessages(final int start, final int cnt, final PromiseMessage whenRes, final ArrayList whenResExt, final SnapshotBuffer sb) { + int base = start; sb.putShortAt(base, (short) cnt); base += 2; if (cnt > 0) { - sb.putLongAt(base, whenRes.serialize(sb)); + if (whenRes.isDelivered()) { + doDeliveredMessage(whenRes, base, sb); + } else { + sb.putLongAt(base, whenRes.forceSerialize(sb)); + } + base += Long.BYTES; if (cnt > 1) { for (int i = 0; i < whenResExt.size(); i++) { - sb.putLongAt(base + i * Long.BYTES, whenResExt.get(i).serialize(sb)); + PromiseMessage msg = whenResExt.get(i); + if (msg.isDelivered()) { + doDeliveredMessage(msg, base + i * Long.BYTES, sb); + } else { + sb.putLongAt(base + i * Long.BYTES, msg.forceSerialize(sb)); + } } base += whenResExt.size() * Long.BYTES; } @@ -114,26 +183,19 @@ private int serializeWhenResolvedMsgs(final int start, final int cnt, return base; } - private int serializeOnErrorMsgs(final int start, final int cnt, - final PromiseMessage onError, final ArrayList onErrorExt, + private void doDeliveredMessage(final PromiseMessage pm, final int location, final SnapshotBuffer sb) { - int base = start; - sb.putShortAt(base, (short) cnt); - base += 2; - if (cnt > 0) { - sb.putLongAt(base, onError.serialize(sb)); - base += Long.BYTES; + assert pm.isDelivered(); - if (cnt > 1) { - for (int i = 0; i < onErrorExt.size(); i++) { - sb.putLongAt(base + i * Long.BYTES, onErrorExt.get(i).serialize(sb)); - } - base += onErrorExt.size() * Long.BYTES; - } + if (pm.getTarget() == sb.getOwner().getCurrentActor()) { + sb.putLongAt(location, pm.forceSerialize(sb)); + } else { + TracingActor ta = (TracingActor) pm.getTarget(); + ta.getSnapshotRecord().farReferenceMessage(pm, sb, location); } - return base; } + @TruffleBoundary // TODO: can we do better than a boundary? private void serializeChainedPromises(final int start, final int cnt, final SPromise chainedProm, final ArrayList chainedPromExt, final SnapshotBuffer sb) { @@ -141,15 +203,13 @@ private void serializeChainedPromises(final int start, final int cnt, sb.putShortAt(base, (short) cnt); base += 2; if (cnt > 0) { - SPromise.getPromiseClass().serialize(chainedProm, sb); - sb.putLongAt(base, sb.getRecord().getObjectPointer(chainedProm)); + handleReferencedPromise(chainedProm, sb, base); base += Long.BYTES; if (cnt > 1) { for (int i = 0; i < chainedPromExt.size(); i++) { - SPromise p = chainedPromExt.get(i); - SPromise.getPromiseClass().serialize(p, sb); - sb.putLongAt(base + i * Long.BYTES, sb.getRecord().getObjectPointer(p)); + SPromise prom = chainedPromExt.get(i); + handleReferencedPromise(prom, sb, base + i * Long.BYTES); } } } @@ -158,8 +218,8 @@ private void serializeChainedPromises(final int start, final int cnt, @Override public SPromise deserialize(final DeserializationBuffer sb) { byte state = sb.get(); - assert state >= 0 && state <= 2; - if (state > 0) { + assert state >= 0 && state <= 3; + if (state == SUCCESS || state == ERROR) { return deserializeCompletedPromise(state, sb); } else { return deserializeUnresolvedPromise(sb); @@ -168,22 +228,47 @@ public SPromise deserialize(final DeserializationBuffer sb) { private SPromise deserializeCompletedPromise(final byte state, final DeserializationBuffer sb) { + int ownerId = sb.getInt(); + Actor owner = SnapshotBackend.lookupActor(ownerId); Object value = sb.getReference(); + int resolver = sb.getInt(); - SPromise p = SPromise.createResolved( - EventualMessage.getActorCurrentMessageIsExecutionOn(), value, - state == 1 ? Resolution.SUCCESSFUL : Resolution.ERRONEOUS); + SPromise p = SPromise.createResolved(owner, value, + state == SUCCESS ? Resolution.SUCCESSFUL : Resolution.ERRONEOUS); + ((STracingPromise) p).setResolvingActorForSnapshot(resolver); if (DeserializationBuffer.needsFixup(value)) { sb.installFixup(new PromiseValueFixup(p)); } + int whenResolvedCnt = sb.getShort(); + for (int i = 0; i < whenResolvedCnt; i++) { + PromiseMessage pm = (PromiseMessage) sb.getReference(); + p.registerWhenResolvedUnsynced(pm); + } + + int onErrorCnt = sb.getShort(); + for (int i = 0; i < onErrorCnt; i++) { + PromiseMessage pm = (PromiseMessage) sb.getReference(); + p.registerOnErrorUnsynced(pm); + } + + int chainedPromCnt = sb.getShort(); + for (int i = 0; i < chainedPromCnt; i++) { + Object remoteObj = sb.getReference(); + if (DeserializationBuffer.needsFixup(remoteObj)) { + sb.installFixup(new ChainedPromiseFixup((STracingPromise) p)); + } else { + initialiseChainedPromise((STracingPromise) p, (SPromise) remoteObj); + } + } return p; } private SPromise deserializeUnresolvedPromise(final DeserializationBuffer sb) { - SPromise promise = SPromise.createPromise( - EventualMessage.getActorCurrentMessageIsExecutionOn(), false, false, null); + int ownerId = sb.getInt(); + Actor owner = SnapshotBackend.lookupActor(ownerId); + SPromise promise = SPromise.createPromise(owner, false, false, null); // These messages aren't referenced by anything else, no need for fixup int whenResolvedCnt = sb.getShort(); @@ -207,6 +292,17 @@ private SPromise deserializeUnresolvedPromise(final DeserializationBuffer sb) { return promise; } + private static void initialiseChainedPromise(final STracingPromise p, + final SPromise remote) { + boolean complete = remote.isCompleted(); + int resolver = p.getResolvingActor(); + + p.addChainedPromise(remote); + remote.resolveFromSnapshot(p.getValueForSnapshot(), p.getResolutionStateUnsync(), + SnapshotBackend.lookupActor(resolver), !complete); + ((STracingPromise) remote).setResolvingActorForSnapshot(resolver); + } + public static class PromiseValueFixup extends FixupInformation { private final SPromise promise; @@ -219,6 +315,19 @@ public void fixUp(final Object o) { promise.setValueFromSnapshot(o); } } + + public static class ChainedPromiseFixup extends FixupInformation { + private final STracingPromise parent; + + public ChainedPromiseFixup(final STracingPromise parent) { + this.parent = parent; + } + + @Override + public void fixUp(final Object o) { + initialiseChainedPromise(parent, (SPromise) o); + } + } } /** @@ -228,31 +337,14 @@ public void fixUp(final Object o) { * If Identity of Resolvers becomes an issue, just add the actor information and turn into a * singleton when deserializing */ - @GenerateNodeFactory public abstract static class ResolverSerializationNode extends AbstractSerializationNode { - public ResolverSerializationNode(final ClassFactory classFact) { - super(classFact); - } @Specialization public void doResolver(final SResolver resolver, final SnapshotBuffer sb) { - int base = sb.addObject(resolver, classFact, Long.BYTES); + int base = sb.addObject(resolver, SResolver.getResolverClass(), Long.BYTES); SPromise prom = resolver.getPromise(); - if (prom.getOwner() == sb.getOwner().getCurrentActor()) { - if (!sb.getRecord().containsObject(prom)) { - SPromise.getPromiseClass().serialize(prom, sb); - } - sb.putLongAt(base, sb.getRecord().getObjectPointer(prom)); - } else { - // The Promise belong to another Actor - TracingActor ta = (TracingActor) prom.getOwner(); - if (!ta.getSnapshotRecord().containsObject(ta)) { - ta.getSnapshotRecord().farReference(prom, sb, base); - } else { - sb.putLongAt(base, ta.getSnapshotRecord().getObjectPointer(prom)); - } - } + handleReferencedPromise(prom, sb, base); } @Override diff --git a/tests/dym/expected-results.tar.bz2 b/tests/dym/expected-results.tar.bz2 index 26e0eb051..b7134c364 100644 Binary files a/tests/dym/expected-results.tar.bz2 and b/tests/dym/expected-results.tar.bz2 differ diff --git a/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java b/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java index 1de2d6734..d407206f6 100644 --- a/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java +++ b/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java @@ -28,7 +28,7 @@ public SafepointPhaserTest() { instanceClass = new SClass(null); factory = new ClassFactory(null, null, instanceSlots, null, false, false, false, null, - false, null, null); + false, null); instanceClass.initializeStructure(null, null, null, false, false, false, factory); layout = factory.getInstanceLayout(); diff --git a/tests/snapshot-replay/test.sh b/tests/snapshot-replay/test.sh new file mode 100755 index 000000000..4f543ca40 --- /dev/null +++ b/tests/snapshot-replay/test.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# quit on first error +set -e +iterations=1 + +if [ "$1" = "1" ] +then + declare -a Savina=( + "PingPong $iterations 0 40000" + "Counting $iterations 0 50000" + "ForkJoinThroughput $iterations 0 300:60" + "ForkJoinActorCreation $iterations 0 4000" + "ThreadRing $iterations 0 100:10000" + "Chameneos $iterations 0 100:10000" + "BigContention $iterations 0 20:12" + "ConcurrentDictionary $iterations 0 5:100:5" + "ConcurrentSortedLinkedList $iterations 0 10:1500:10:1" + "ProducerConsumerBoundedBuffer $iterations 0 40:10:10:60" + "Philosophers $iterations 0 20:5000" + + ) +else + declare -a Savina=( + "SleepingBarber $iterations 0 2500:1000:1000:1000" + "CigaretteSmokers $iterations 0 10000:200" + "LogisticsMapSeries $iterations 0 25000:10:346" + "BankTransaction $iterations 0 500:10000" + "RadixSort $iterations 0 50000:65536:74755" + "UnbalancedCobwebbedTree $iterations 0 100000:10:500:100" + "TrapezoidalApproximation $iterations 0 100:1000000:1:5" + "AStarSearch $iterations 0 100:20" + "NQueens $iterations 0 20:10:4" + ) +fi + +## Determine absolute path of script +pushd `dirname $0` > /dev/null +SCRIPT_PATH=`pwd` +popd > /dev/null + +SOM_DIR=$SCRIPT_PATH/../.. + +for args in "${Savina[@]}" +do + counter=1 + while [ $counter -le 10 ] + do + + echo "$counter. $args" + echo "Tracing:" + $SOM_DIR/som -EG -as -at -JXmx3000m -JXss8192k core-lib/Benchmarks/AsyncHarness.ns SavinaSnap.$args + echo "" + echo "Replay:" + $SOM_DIR/som -EG -as -r -JXmx2000m -JXss8192k -vmd core-lib/Benchmarks/AsyncHarness.ns SavinaSnap.$args + echo "" + echo "========================================================" + echo "" + ((counter++)) + done +done diff --git a/tests/snapshot/test.sh b/tests/snapshot/test.sh index 367189770..d8ae0affd 100755 --- a/tests/snapshot/test.sh +++ b/tests/snapshot/test.sh @@ -36,7 +36,7 @@ for args in "${Savina[@]}" do echo "$args" echo "Tracing:" - $SOM_DIR/som -G -JXss4096k -at -as -sam -TF core-lib/Benchmarks/AsyncHarness.ns Savina.$args + $SOM_DIR/som -G -JXss8096k -at -as -sam -TF -sind=0 core-lib/Benchmarks/AsyncHarness.ns Savina.$args echo "" echo "========================================================" echo ""