From d6a4be75091b1176f3db07ad551d97970b1806b4 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Fri, 23 Nov 2018 13:05:02 +0100 Subject: [PATCH 01/80] Implement replay from snapshot --- core-lib/Benchmarks/SavinaSnap.ns | 2622 +++++++++++++++++ src/som/Launcher.java | 7 +- src/som/VM.java | 7 +- src/som/compiler/MixinDefinition.java | 3 +- src/som/interpreter/actors/Actor.java | 24 +- .../interpreter/actors/EventualMessage.java | 19 +- src/som/interpreter/actors/SFarReference.java | 4 +- src/som/interpreter/actors/SPromise.java | 12 +- .../objectstorage/ClassFactory.java | 28 +- src/som/primitives/SystemPrims.java | 6 +- src/som/vm/ObjectSystem.java | 40 + src/som/vm/Symbols.java | 19 + src/som/vm/VmSettings.java | 3 + src/som/vmobjects/SClass.java | 38 +- src/som/vmobjects/SSymbol.java | 22 + src/tools/concurrency/TraceParser.java | 8 +- .../concurrency/TracingActivityThread.java | 18 +- src/tools/concurrency/TracingActors.java | 31 +- src/tools/concurrency/TracingBackend.java | 6 +- src/tools/snapshot/SnapshotBackend.java | 356 ++- src/tools/snapshot/SnapshotBuffer.java | 20 +- src/tools/snapshot/SnapshotRecord.java | 7 - .../DeserializationBuffer.java | 211 +- .../deserialization/SnapshotParser.java | 242 ++ .../nodes/AbstractArraySerializationNode.java | 30 +- .../nodes/AbstractSerializationNode.java | 16 +- .../nodes/BlockSerializationNode.java | 84 +- .../nodes/CachedSerializationNode.java | 2 +- .../nodes/MessageSerializationNode.java | 20 +- .../nodes/ObjectSerializationNodes.java | 60 +- .../nodes/PrimitiveSerializationNodes.java | 93 +- .../nodes/PromiseSerializationNodes.java | 27 +- 32 files changed, 3832 insertions(+), 253 deletions(-) create mode 100644 core-lib/Benchmarks/SavinaSnap.ns create mode 100644 src/tools/snapshot/deserialization/SnapshotParser.java diff --git a/core-lib/Benchmarks/SavinaSnap.ns b/core-lib/Benchmarks/SavinaSnap.ns new file mode 100644 index 000000000..6c90fa74e --- /dev/null +++ b/core-lib/Benchmarks/SavinaSnap.ns @@ -0,0 +1,2622 @@ +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 = 2500 ifTrue:[ + 'snapshot' println. + 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 = ( + completionRes resolve: cnt = limit + ) + ) + + public class CountingActor = ( + | private count ::= 0. | + ) ( + public increment = ( + count:: count + 1. + ) + + public requestCount: requester = ( + requester <-: count: count + ) + ) + + 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 + ]. + + numMessages timesRepeat: [ + 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. + + numActors timesRepeat: [ + | promisePair | + 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 > 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:: 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 ] + ]. + + ^ 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. + ] + ) + + 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. + ] + )( + 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. + ] + )( + 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 ]. + + ^ 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 = ( + numHaircuts timesRepeat: [ + 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 + 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. + + 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 | 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. + | + )( + 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 = 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. + + ^ 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 ] + ) + + 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 | + 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. + + 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 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. + ^ 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 + )( + 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/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..8a1c8a576 100644 --- a/src/som/VM.java +++ b/src/som/VM.java @@ -293,6 +293,7 @@ public void initalize(final SomLanguage lang) throws IOException { assert objectSystem == null; objectSystem = new ObjectSystem(new SourcecodeCompiler(lang), structuralProbe, this); + objectSystem.loadKernelAndPlatform(options.platformFile, options.kernelFile); assert vmMirror == null : "VM seems to be initialized already"; @@ -316,7 +317,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..4a845c90f 100644 --- a/src/som/compiler/MixinDefinition.java +++ b/src/som/compiler/MixinDefinition.java @@ -886,7 +886,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/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 208c0dda6..3f755e8bf 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -176,6 +176,14 @@ public synchronized void sendInitialStartMessage(final EventualMessage msg, doSend(msg, pool); } + public synchronized void sendSnapshotMessage(final EventualMessage msg) { + if (firstMessage != null) { + appendToMailbox(msg); + } else { + firstMessage = msg; + } + } + private void doSend(final EventualMessage msg, final ForkJoinPool actorPool) { assert msg.getTarget() == this; @@ -279,14 +287,22 @@ protected void processCurrentMessages(final ActorProcessingThread currentThread, if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { SnapshotBuffer sb = currentThread.getSnapshotBuffer(); sb.getRecord().handleTodos(sb); - firstMessage.serialize(sb); + long loc = firstMessage.serialize(sb); + if (loc != -1) { + sb.getOwner().addMessageLocation(((TracingActor) actor).getActorId(), + sb.calculateReference(loc)); + } } execute(firstMessage, currentThread, dbg); if (size > 1) { for (EventualMessage msg : mailboxExtension) { if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { - msg.serialize(currentThread.getSnapshotBuffer()); + long loc = msg.serialize(currentThread.getSnapshotBuffer()); + if (loc != -1) { + currentThread.addMessageLocation(((TracingActor) actor).getActorId(), + currentThread.getSnapshotBuffer().calculateReference(loc)); + } } execute(msg, currentThread, dbg); } @@ -338,6 +354,10 @@ private boolean getCurrentMessagesOrCompleteExecution() { return true; } + + public TraceActorContextNode getActorContextNode() { + return tracer; + } } @TruffleBoundary diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index 994cea940..cffee1e3f 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -112,7 +112,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 +133,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 +238,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(); } } @@ -284,6 +284,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,7 +304,7 @@ 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) { + if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.REPLAY) { this.messageId = Math.min(this.messageId, ActorProcessingThread.currentThread().getSnapshotId()); } @@ -375,6 +378,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 +396,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(); } } diff --git a/src/som/interpreter/actors/SFarReference.java b/src/som/interpreter/actors/SFarReference.java index e43701a8b..3c3ce89af 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; @@ -52,8 +51,7 @@ 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.getSerializer().replace(FarRefSerializationNodeFactory.create(cls)); } } } diff --git a/src/som/interpreter/actors/SPromise.java b/src/som/interpreter/actors/SPromise.java index 074ef4d86..3bd7f0cb9 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; @@ -142,8 +141,8 @@ 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.getSerializer() + .replace(PromiseSerializationNodeFactory.create(promiseClass)); } } @@ -327,6 +326,9 @@ public int getResolvingActor() { return resolvingActor; } + public void setResolvingActorForSnapshot(final int resolver) { + this.resolvingActor = resolver; + } } public static final class SMedeorPromise extends STracingPromise { @@ -380,8 +382,8 @@ public static void setSOMClass(final SClass cls) { resolverClass = cls; if (VmSettings.SNAPSHOTS_ENABLED) { - ClassFactory group = resolverClass.getInstanceFactory(); - group.getSerializer().replace(ResolverSerializationNodeFactory.create(group)); + resolverClass.getSerializer() + .replace(ResolverSerializationNodeFactory.create(resolverClass)); } } diff --git a/src/som/interpreter/objectstorage/ClassFactory.java b/src/som/interpreter/objectstorage/ClassFactory.java index 639764042..8305308c1 100644 --- a/src/som/interpreter/objectstorage/ClassFactory.java +++ b/src/som/interpreter/objectstorage/ClassFactory.java @@ -1,5 +1,7 @@ package som.interpreter.objectstorage; +import java.util.concurrent.atomic.AtomicInteger; + import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; @@ -15,7 +17,6 @@ import som.vmobjects.SClass; import som.vmobjects.SSymbol; import tools.snapshot.nodes.AbstractSerializationNode; -import tools.snapshot.nodes.SerializerRootNode; /** @@ -61,7 +62,8 @@ public final class ClassFactory { private final ClassFactory classClassFactory; - protected final SerializerRootNode serializationRoot; + protected final AtomicInteger identityGen; + private final NodeFactory serializerFactory; public ClassFactory(final SSymbol name, final MixinDefinition mixinDef, final EconomicSet instanceSlots, @@ -88,10 +90,11 @@ public ClassFactory(final SSymbol name, final MixinDefinition mixinDef, this.superclassAndMixins = superclassAndMixins; if (VmSettings.SNAPSHOTS_ENABLED) { - this.serializationRoot = - new SerializerRootNode(serializerFactory.createNode(this)); + this.serializerFactory = serializerFactory; + this.identityGen = new AtomicInteger(0); } else { - this.serializationRoot = null; + this.serializerFactory = null; + this.identityGen = null; } VM.callerNeedsToBeOptimized( @@ -135,14 +138,14 @@ public SClass[] getSuperclassAndMixins() { return superclassAndMixins; } - public AbstractSerializationNode getSerializer() { - return serializationRoot.getSerializer(); - } - public MixinDefinition getMixinDefinition() { return mixinDef; } + public NodeFactory getSerializerFactory() { + return this.serializerFactory; + } + /** * 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 +203,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/primitives/SystemPrims.java b/src/som/primitives/SystemPrims.java index 3f917372e..eae144dd3 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(); } @@ -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/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index ab7c59be9..5365c232a 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -45,7 +45,10 @@ 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; @@ -100,6 +103,11 @@ public ObjectSystem(final SourcecodeCompiler compiler, this.compiler = compiler; structuralProbe = probe; loadedModules = EconomicMap.create(); + if (VmSettings.SNAPSHOTS_ENABLED) { + // List is not modified, only used at program termination to know which modules need to + // be loaded in replay + SnapshotBackend.registerLoadedModules(loadedModules); + } this.vm = vm; } @@ -154,6 +162,8 @@ public MixinDefinition loadModule(final Source source) throws IOException { return loadedModules.get(uri); } + // System.out.println("Loaded: " + uri); + MixinDefinition module; try { module = compiler.compileModule(source, structuralProbe); @@ -165,6 +175,10 @@ public MixinDefinition loadModule(final Source source) throws IOException { } } + public EconomicMap getLoadedModulesForSnapshot() { + return loadedModules; + } + private SObjectWithoutFields constructVmMirror() { EconomicMap vmMirrorMethods = primitives.takeVmMirrorPrimitives(); SClass vmMirrorClass = constructPrimitiveClass(vmMirrorMethods); @@ -481,6 +495,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 +547,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 +588,24 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m } } + @TruffleBoundary + public int executeApplicationFromSnapshot(final SObjectWithoutFields vmMirror) { + mainThreadCompleted = new CompletableFuture<>(); + System.out.println("Attemting to execute from Snapshot"); + ObjectTransitionSafepoint.INSTANCE.register(); + Object platform = platformModule.instantiateObject(platformClass, vmMirror); + ObjectTransitionSafepoint.INSTANCE.unregister(); + + SnapshotBackend.initialize(vm); + SnapshotParser.inflate(vm); + // System.out.println("Done with inflation!!!"); + ReplayActor.scheduleAllActors(vm.getActorPool()); + // System.out.println("Done scheduling actors"); + + 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..72283e8f2 100644 --- a/src/som/vm/VmSettings.java +++ b/src/som/vm/VmSettings.java @@ -39,6 +39,8 @@ public class VmSettings implements Settings { public static final String BASE_DIRECTORY; + public static final boolean SNAPSHOT_REPLAY; + static { String prop = System.getProperty("som.threads"); if (prop == null) { @@ -65,6 +67,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; diff --git a/src/som/vmobjects/SClass.java b/src/som/vmobjects/SClass.java index b5e133502..f1a105937 100644 --- a/src/som/vmobjects/SClass.java +++ b/src/som/vmobjects/SClass.java @@ -45,9 +45,11 @@ import som.interpreter.objectstorage.ObjectLayout; import som.vm.VmSettings; import som.vm.constants.Classes; +import tools.concurrency.TracingActivityThread; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.nodes.AbstractSerializationNode; +import tools.snapshot.nodes.SerializerRootNode; // TODO: should we move more of that out of SClass and use the corresponding @@ -69,6 +71,8 @@ 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 SerializerRootNode serializationRoot; protected final SObjectWithClass enclosingObject; private final MaterializedFrame context; @@ -147,6 +151,7 @@ public EconomicSet getInstanceSlots() { public void initializeClass(final SSymbol name, final SClass superclass) { assert (this.name == null || this.name == name) && (this.superclass == null || this.superclass == superclass) : "Should only be initialized once"; + this.name = name; this.superclass = superclass; } @@ -191,14 +196,30 @@ 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 + && Thread.currentThread() instanceof TracingActivityThread) { + // enclosingObject.getSOMClass().serialize(enclosingObject, + // TracingActivityThread.currentThread().getSnapshotBuffer()); + // long outer = SnapshotBackend.getCurrentActor().getSnapshotRecord() + // .getObjectPointer(enclosingObject); + // we can find out the classslot identity by looking for a class with an outer that + // matches the current class... + // -> no need to go through all the slots in search of initialized class slots. + SnapshotBackend.registerClassEnclosure(this); + } + + this.serializationRoot = + new SerializerRootNode(classFactory.getSerializerFactory().createNode(this)); + + } // 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); } } @@ -362,6 +383,11 @@ public void serialize(final Object o, final SnapshotBuffer sb) { } public AbstractSerializationNode getSerializer() { - return instanceClassGroup.getSerializer(); + return serializationRoot.getSerializer(); + } + + public int getIdentity() { + assert identity != 0; + return identity; } } diff --git a/src/som/vmobjects/SSymbol.java b/src/som/vmobjects/SSymbol.java index cd51e9815..38975b9b5 100644 --- a/src/som/vmobjects/SSymbol.java +++ b/src/som/vmobjects/SSymbol.java @@ -53,6 +53,28 @@ 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.KOMPOS_TRACING || VmSettings.ACTOR_TRACING) { + TracingBackend.logSymbol(this); + } + + 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..be634a826 100644 --- a/src/tools/concurrency/TraceParser.java +++ b/src/tools/concurrency/TraceParser.java @@ -42,10 +42,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); diff --git a/src/tools/concurrency/TracingActivityThread.java b/src/tools/concurrency/TracingActivityThread.java index 2eb0e5718..7e99872f6 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) { @@ -165,6 +169,11 @@ public final void addExternalData(final Object data) { } } + public final void addMessageLocation(final int actorId, final long messageAdress) { + messageLocations.add((long) actorId); + messageLocations.add(messageAdress); + } + @Override protected void onStart() { super.onStart(); @@ -181,7 +190,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,7 +224,13 @@ public byte getSnapshotId() { } private void newSnapshot() { + TracingActor ta = (TracingActor) ((ActorProcessingThread) this).getCurrentActor(); + TraceActorContextNode tracer = ta.getActorContextNode();// get from ta? traceBuffer.swapStorage(); + if (tracer != null) { + tracer.trace(ta); + } + if (extIndex != 0) { TracingBackend.addExternalData(externalData, this); externalData = new Object[EXTERNAL_BUFFER_SIZE]; @@ -225,6 +240,7 @@ private void newSnapshot() { // 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..739c32b21 100644 --- a/src/tools/concurrency/TracingActors.java +++ b/src/tools/concurrency/TracingActors.java @@ -1,11 +1,11 @@ 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; @@ -25,6 +25,7 @@ import tools.concurrency.TraceParser.PromiseMessageRecord; import tools.debugger.WebDebugger; import tools.replay.actors.ExternalMessage; +import tools.replay.nodes.TraceActorContextNode; import tools.snapshot.SnapshotRecord; import tools.snapshot.deserialization.DeserializationBuffer; @@ -67,6 +68,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; } @@ -124,7 +133,7 @@ public static final class ReplayActor extends TracingActor { static { if (VmSettings.REPLAY) { - actorList = new WeakHashMap<>(); + actorList = new HashMap<>(); } } @@ -168,7 +177,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 +228,16 @@ public synchronized void send(final EventualMessage msg, final ForkJoinPool acto } } + public static void scheduleAllActors(final ForkJoinPool actorPool) { + for (ReplayActor ra : actorList.values()) { + + if (ra.firstMessage != null && !ra.isExecuting) { + ra.isExecuting = true; + ra.execute(actorPool); + } + } + } + /** * Prints a list of expected Messages and remaining mailbox content. * @@ -400,8 +423,8 @@ protected void processCurrentMessages(final ActorProcessingThread currentThread, final WebDebugger dbg) { assert actor instanceof ReplayActor; assert size > 0; - final ReplayActor a = (ReplayActor) actor; + Queue todo = determineNextMessages(a.leftovers); for (EventualMessage msg : todo) { diff --git a/src/tools/concurrency/TracingBackend.java b/src/tools/concurrency/TracingBackend.java index 9fe60496e..9b83fabfa 100644 --- a/src/tools/concurrency/TracingBackend.java +++ b/src/tools/concurrency/TracingBackend.java @@ -581,10 +581,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/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index ea7fa5b1c..912b00d16 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -1,31 +1,60 @@ 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.ConcurrentLinkedQueue; import org.graalvm.collections.EconomicMap; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; + +import som.VM; +import som.compiler.MixinDefinition; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage; +import som.interpreter.actors.SPromise; +import som.interpreter.nodes.InstantiationNode.ClassInstantiationNode; +import som.interpreter.objectstorage.ClassFactory; +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; +import tools.snapshot.nodes.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory; 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 ArrayList classEnclosures; + + // 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 +62,33 @@ public class SnapshotBackend { symbolDictionary = EconomicMap.create(); probe = new StructuralProbe(); buffers = new ConcurrentLinkedQueue<>(); + messages = new ConcurrentLinkedQueue<>(); + classEnclosures = new ArrayList<>(); + // 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; buffers = new ConcurrentLinkedQueue<>(); + messages = new ConcurrentLinkedQueue<>(); + classEnclosures = new ArrayList<>(); } else { classDictionary = null; symbolDictionary = null; probe = null; buffers = null; + messages = null; + classEnclosures = null; } } + public static void initialize(final VM vm) { + SnapshotBackend.vm = vm; + } + public static SSymbol getSymbolForId(final short id) { return symbolDictionary.get(id); } @@ -55,29 +98,151 @@ public static void registerSymbol(final SSymbol sym) { symbolDictionary.put(sym.getSymbolId(), sym); } - public static void registerClass(final SSymbol sym, final SClass clazz) { + public static 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)) { + // this method isn't designed for fixup + 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); + + // 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}); + + // System.out.println("creating Class" + mixin.getIdentifier() + " : " + (short) id); + + ClassFactory factory = mixin.createClassFactory(superclassAndMixins, false, false, false, + UninitializedObjectSerializationNodeFactory.getInstance()); + + SClass result = new SClass(enclosingObject, + ClassInstantiationNode.instantiateMetaclassClass(factory, enclosingObject)); + factory.initializeClass(result); + + // Step 4: fixup + Object current = classDictionary.get(id); + if (current instanceof LinkedList) { + LinkedList todo = (LinkedList) current; + DeserializationBuffer db = SnapshotParser.getDeserializationBuffer(); + for (long ref : todo) { + System.out.println("fixing" + result); + db.fixUpIfNecessary(ref, result); + } + } + + classDictionary.put(id, 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() { @@ -97,20 +262,26 @@ 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); } public static StructuralProbe getProbe() { @@ -118,14 +289,34 @@ 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. + */ 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); + writeSymbolTable(); + + // WriteHeapMap + writeHeapMap(fos, offset); + + // Write Heap while (!buffers.isEmpty()) { SnapshotBuffer sb = buffers.poll(); fos.getChannel().write(ByteBuffer.wrap(sb.getRawBuffer(), 0, sb.position())); @@ -135,4 +326,135 @@ public static void writeSnapshot() { throw new RuntimeException(e1); } } + + private static int writeLoadedModules(final FileOutputStream fos) throws IOException { + int size = (loadedModules.size() + 1) * Short.BYTES; + ByteBuffer bb = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + // by adding the uri strings to the symbol table we can use a compact represenation as part + // of the snap file. + + bb.putShort((short) loadedModules.size()); + for (MixinDefinition module : loadedModules.getValues()) { + bb.putShort(module.getIdentifier().getSymbolId()); + } + + bb.rewind(); + fos.getChannel().write(bb); + fos.flush(); + return size; + } + + private static int writeClassEnclosures(final FileOutputStream fos) throws IOException { + int size = (classEnclosures.size() * 2 + 1) * Long.BYTES; + ByteBuffer bb = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + + bb.putLong(classEnclosures.size()); + for (SClass clazz : classEnclosures) { + bb.putLong(clazz.getIdentity()); + SObjectWithClass outer = clazz.getEnclosingObject(); + // TODO if necessary keep actual ownership information, for now we assume all the class + // creation is happening in the main actor. + TracingActor owner = (TracingActor) vm.getMainActor(); + if (!owner.getSnapshotRecord().containsObject(outer)) { + // just use the first buffer available; that object isn't used anywhere else + outer.getSOMClass().serialize(outer, buffers.peek()); + } + bb.putLong(owner.getSnapshotRecord().getObjectPointer(outer)); + } + + 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 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 registerClassEnclosure(final SClass clazz) { + synchronized (classEnclosures) { + classEnclosures.add(clazz); + } + } } diff --git a/src/tools/snapshot/SnapshotBuffer.java b/src/tools/snapshot/SnapshotBuffer.java index 7e63417fa..0a7238630 100644 --- a/src/tools/snapshot/SnapshotBuffer.java +++ b/src/tools/snapshot/SnapshotBuffer.java @@ -1,9 +1,9 @@ package tools.snapshot; import som.interpreter.actors.Actor.ActorProcessingThread; -import som.interpreter.objectstorage.ClassFactory; 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 +13,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; @@ -38,19 +38,18 @@ 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) { + public int addObject(final Object o, final SClass clazz, final int payload) { assert !getRecord().containsObject(o) : "Object serialized multiple times"; int oldPos = this.position; getRecord().addObjectEntry(o, calculateReference(oldPos)); - this.putShortAt(this.position, - classFact.getIdentifier().getSymbolId()); + this.putIntAt(this.position, clazz.getIdentity()); this.position += CLASS_ID_SIZE + payload; return oldPos + CLASS_ID_SIZE; } - public int addObjectWithFields(final Object o, final ClassFactory classFact, + public int addObjectWithFields(final Object o, final SClass clazz, final int fieldCnt) { assert fieldCnt < MAX_FIELD_CNT; assert !getRecord().containsObject(o) : "Object serialized multiple times"; @@ -58,8 +57,7 @@ public int addObjectWithFields(final Object o, final ClassFactory classFact, int oldPos = this.position; getRecord().addObjectEntry(o, calculateReference(oldPos)); - this.putShortAt(this.position, - classFact.getIdentifier().getSymbolId()); + this.putIntAt(this.position, clazz.getIdentity()); this.position += CLASS_ID_SIZE + (FIELD_SIZE * fieldCnt); return oldPos + CLASS_ID_SIZE; } @@ -68,10 +66,10 @@ public int addMessage(final int payload) { // 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(); + // 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..309c39329 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -3,7 +3,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.EconomicSet; import som.interpreter.Types; @@ -14,7 +13,6 @@ public class SnapshotRecord { * We can get the location of the serialized object in the trace */ private final EconomicMap entries; - private final EconomicSet messageOffsets; /** * This list is used to keep track of references to unserialized objects in the actor owning @@ -29,7 +27,6 @@ public class SnapshotRecord { public SnapshotRecord() { this.entries = EconomicMap.create(); - this.messageOffsets = EconomicSet.create(); this.externalReferences = new ConcurrentLinkedQueue<>(); } @@ -45,10 +42,6 @@ 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); - } - public void addObjectEntry(final Object o, final long offset) { synchronized (entries) { entries.put(o, offset); diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index afffa4a67..ea2ecaa1e 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -1,91 +1,159 @@ package tools.snapshot.deserialization; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.channels.FileChannel; 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.SObject; import tools.snapshot.SnapshotBackend; import tools.snapshot.deserialization.FixupInformation.FixupList; +import tools.snapshot.nodes.ObjectSerializationNodes.SObjectSerializationNode.SlotFixup; public class DeserializationBuffer { - private final ByteBuffer wrapped; + protected final ByteBuffer buffer; private final EconomicMap deserialized; private long lastRef; + int depth = 0; 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(); + } + + public DeserializationBuffer(final ByteBuffer buffer) { + this.buffer = buffer; deserialized = EconomicMap.create(); } 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); + } + + private void printPosition(final long current) { + // System.out.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)); + // System.out.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 Object deserializeWithoutContext(final long current) { + lastRef = current; + printPosition(current); + this.position(current); // 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); - fixUpIfNecessary(current, o); - deserialized.put(current, o); + if (!deserialized.containsKey(current)) { + deserialized.put(current, null); + } + + int cId = getInt(); + printClass(cId); + + depth++; + SClass clazz = SnapshotBackend.lookupClass(cId); + + Object o = clazz.getSerializer().deserialize(this); + + depth--; + if (o != null) { + fixUpIfNecessary(current, o); + deserialized.put(current, o); + } return o; } public Object getReference() { long reference = getLong(); + lastRef = reference; if (!deserialized.containsKey(reference)) { - int current = position(); + long current = position(); + printPosition(reference); - deserialized.put(reference, null); + if (!deserialized.containsKey(reference)) { + 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); + int cId = getInt(); + printClass(cId); + depth++; + SClass clazz = SnapshotBackend.lookupClass(cId); + Object o = clazz.getSerializer().deserialize(this); + depth--; // continue with current object position(current); fixUpIfNecessary(reference, o); deserialized.put(reference, o); return o; } else { + printPosition(reference); 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,7 +167,8 @@ public synchronized void installFixup(final FixupInformation fi) { } } - private synchronized void fixUpIfNecessary(final long reference, final Object result) { + public synchronized void fixUpIfNecessary(final long reference, final Object result) { + assert result != null; Object ref = deserialized.get(reference); if (ref instanceof FixupList) { // we have fixup information, this means that this object is part of a circular @@ -110,15 +179,97 @@ private synchronized void fixUpIfNecessary(final long reference, final Object re } } - public int position() { - return wrapped.position(); + public synchronized void installObjectFixup(final SObject o, final CachedSlotWrite write) { + deserialized.put(lastRef, o); + 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); } - public void position(final int newPosition) { - wrapped.position(newPosition); + protected void ensureRemaining(final int bytes) + throws IOException { + assert buffer.remaining() >= bytes; } - public Actor getActor() { - return null; + public long position() { + return buffer.position(); + } + + public void position(final long newPosition) { + buffer.position((int) newPosition); + } + + 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..d6b530c19 --- /dev/null +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -0,0 +1,242 @@ +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 org.graalvm.collections.EconomicMap; + +import som.VM; +import som.interpreter.actors.EventualMessage; +import som.interpreter.actors.SPromise; +import som.vm.Symbols; +import som.vm.VmSettings; +import som.vm.constants.Nil; +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 final EconomicMap heapOffsets; + private final EconomicMap> messageLocations; + private SPromise resultPromise; + private ReplayActor currentActor; + private VM vm; + private EconomicMap outerMap; + private DeserializationBuffer db; + + private SnapshotParser(final VM vm) { + this.vm = vm; + this.heapOffsets = EconomicMap.create(); + this.messageLocations = EconomicMap.create(); + this.outerMap = EconomicMap.create(); + } + + // 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); + int actorId = (int) b.getLong(); + long location = b.getLong(); + + if (!messageLocations.containsKey(actorId)) { + messageLocations.put(actorId, new ArrayList<>()); + } + messageLocations.get(actorId).add(location); + } + + long numOuters = b.getLong(); + for (int i = 0; i < numOuters; i++) { + ensureRemaining(Long.BYTES * 2, b, channel); + int identity = (int) b.getLong(); + long outer = b.getLong(); + outerMap.put(identity, outer); + } + + // ensureRemaining(Short.BYTES, b, channel); + // short numModules = b.getShort(); + // for (int i = 0; i < numModules; i++) { + // ensureRemaining(Short.BYTES, b, channel); + // short symId = b.getShort(); + // SSymbol sym = SnapshotBackend.getSymbolForId(symId); + // URI uri = new URI(sym.getString()); + // String[] components = sym.getString().split(":"); + // assert components.length == 2; + // String path = components[0]; + // + // // I think extension modules are not added to the list we use... + // // for now this is not an issue but later we may need to solve this for acme + // // My understanding is that there is exactly one SClass object, which is returned when + // // loading the module. + // // By loading the module multiple times you would get a new SClass every time. + // if (path.endsWith(SystemPrims.EXTENSION_EXT)) { + // vm.loadExtensionModule(path); + // } else { + // MixinDefinition module; + // module = vm.loadModule(path); + // // TODO looks like the module paths we try to load are problematic. + // // probably a hashtag too many. + // } + // } + + 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. + + // EconomicMap actors = EconomicMap.create(); + + // lets create the Actor objects first, and add them to our list + for (int id : messageLocations.getKeys()) { + SnapshotBackend.lookupActor(id); + // actors.put(id, ra); + } + + db = new FileDeserializationBuffer(channel); + + // 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()) { + ArrayList locations = messageLocations.get(id); + for (long location : locations) { + // Deserialilze message + currentActor = ReplayActor.getActorWithId(id); + // System.out.println( + // "Message at: " + (((int) location) + getFileOffset(location))); + EventualMessage em = (EventualMessage) db.deserializeWithoutContext(location); + currentActor.sendSnapshotMessage(em); + } + } + + resultPromise = (SPromise) db.getReference(resultPromiseLocation); + if (resultPromise == null) { + resultPromise = (SPromise) db.deserialize(resultPromiseLocation); + } + + 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 + db = null; + } + } + + 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); + return parser.heapOffsets.get(threadId); + } + + public static SObjectWithClass getOuterForClass(final int identity) { + SObjectWithClass result; + + if (parser.outerMap.containsKey(identity)) { + long reference = parser.outerMap.get(identity); + Object o = parser.db.getReference(reference); + long pos = ((int) reference) + SnapshotParser.getFileOffset(reference); + // System.out.print("Outer - " + pos + " in " + (reference >> 48) + " "); + if (!parser.db.allreadyDeserialized(reference)) { + result = (SObjectWithClass) parser.db.deserialize(reference); + } else if (parser.db.needsFixup(o)) { + result = null; + } 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 SPromise getResultPromise() { + assert parser.resultPromise != null; + return parser.resultPromise; + } + + public static DeserializationBuffer getDeserializationBuffer() { + return parser.db; + } +} diff --git a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java index 77d3c22eb..fe66832e4 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,8 @@ 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); + public AbstractArraySerializationNode(final SClass clazz) { + super(clazz); } @Override @@ -35,7 +35,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 +49,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 +63,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 +77,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 +91,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 +102,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; @@ -165,8 +165,8 @@ protected Object parseBackingStorage(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class ArraySerializationNode extends AbstractArraySerializationNode { - public ArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public ArraySerializationNode(final SClass clazz) { + super(clazz); } @Override @@ -179,8 +179,8 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class TransferArraySerializationNode extends ArraySerializationNode { - public TransferArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public TransferArraySerializationNode(final SClass clazz) { + super(clazz); } @Override @@ -193,8 +193,8 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class ValueArraySerializationNode extends ArraySerializationNode { - public ValueArraySerializationNode(final ClassFactory classFact) { - super(classFact); + public ValueArraySerializationNode(final SClass clazz) { + super(clazz); } @Override diff --git a/src/tools/snapshot/nodes/AbstractSerializationNode.java b/src/tools/snapshot/nodes/AbstractSerializationNode.java index ae15bb702..0af7674bd 100644 --- a/src/tools/snapshot/nodes/AbstractSerializationNode.java +++ b/src/tools/snapshot/nodes/AbstractSerializationNode.java @@ -4,16 +4,28 @@ 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 SClass clazz; public final ClassFactory classFact; - public AbstractSerializationNode(final ClassFactory classFact) { + public AbstractSerializationNode(final SClass clazz) { assert VmSettings.SNAPSHOTS_ENABLED; - this.classFact = classFact; + this.clazz = clazz; + this.classFact = clazz.getInstanceFactory(); + } + + /** + * Constructor for CachedSerializationNode. + */ + public AbstractSerializationNode() { + assert VmSettings.SNAPSHOTS_ENABLED; + this.clazz = null; + this.classFact = null; } public abstract void execute(Object o, SnapshotBuffer sb); diff --git a/src/tools/snapshot/nodes/BlockSerializationNode.java b/src/tools/snapshot/nodes/BlockSerializationNode.java index 88fa60431..423b208bb 100644 --- a/src/tools/snapshot/nodes/BlockSerializationNode.java +++ b/src/tools/snapshot/nodes/BlockSerializationNode.java @@ -5,17 +5,19 @@ 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; +import som.vmobjects.SClass; import som.vmobjects.SInvokable; +import som.vmobjects.SSymbol; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.SnapshotRecord; @@ -28,8 +30,8 @@ public abstract class BlockSerializationNode extends AbstractSerializationNode { private static final int SINVOKABLE_SIZE = Short.BYTES; - public BlockSerializationNode(final ClassFactory classFact) { - super(classFact); + public BlockSerializationNode(final SClass clazz) { + super(clazz); } // TODO specialize on different blocks @@ -39,8 +41,9 @@ 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, clazz, SINVOKABLE_SIZE + 2); SInvokable meth = block.getMethod(); + // System.out.println("Block without frame " + meth.getIdentifier()); sb.putShortAt(base, meth.getIdentifier().getSymbolId()); sb.putShortAt(base + 2, (short) 0); } else { @@ -48,12 +51,19 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { Object[] args = mf.getArguments(); - int start = sb.addObject(block, classFact, + int start = sb.addObject(block, clazz, SINVOKABLE_SIZE + ((args.length + fd.getSlots().size()) * Long.BYTES) + 2); int base = start; SInvokable meth = block.getMethod(); + // System.out.println("Block " + meth.getIdentifier()); sb.putShortAt(base, meth.getIdentifier().getSymbolId()); + // System.out.println( + // "with " + args.length + " args at: " + base + " in " + sb.getOwner().getThreadId()); + // for (Object o : args) { + // System.out.println("\t" + o); + // } + sb.putByteAt(base + 2, (byte) args.length); base += 3; @@ -77,6 +87,7 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { // TODO optimization: MaterializedFrameSerialization Nodes that are associated with the // Invokables Frame Descriptor. Possibly use Local Var Read Nodes. Object value = mf.getValue(slot); + // System.out.println("Slot" + slot + value); switch (fd.getFrameSlotKind(slot)) { case Boolean: Classes.booleanClass.serialize(value, sb); @@ -121,15 +132,19 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { @Override public Object deserialize(final DeserializationBuffer bb) { short sinv = bb.getShort(); + SSymbol inv = SnapshotBackend.getSymbolForId(sinv); SInvokable invokable = SnapshotBackend.lookupInvokable(sinv); + assert invokable != null : "Invokable not found"; FrameDescriptor fd = ((Method) invokable.getInvokable()).getLexicalScope().getOuterMethod() .getMethod().getFrameDescriptor(); + // TODO Nullpointer when getting FD // read num args int numArgs = bb.get(); Object[] args = new Object[numArgs]; + // System.out.println("Block with " + inv); // read args for (int i = 0; i < numArgs; i++) { Object arg = bb.getReference(); @@ -140,6 +155,8 @@ public Object deserialize(final DeserializationBuffer bb) { } } + // System.out.println(Arrays.toString(args)); + MaterializedFrame frame = Truffle.getRuntime().createMaterializedFrame(args, fd); int numSlots = bb.get(); @@ -154,38 +171,43 @@ public Object deserialize(final DeserializationBuffer bb) { 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; + } + + boolean illegal = fd.getFrameSlotKind(slot) == FrameSlotKind.Illegal; - switch (fd.getFrameSlotKind(slot)) { - case Boolean: - frame.setBoolean(slot, (boolean) o); - break; - case Double: - frame.setDouble(slot, (double) o); - break; - case 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; - } - frame.setObject(slot, o); - break; - case Illegal: - // uninitialized variable, uses default - frame.setObject(slot, o); - break; - default: - throw new IllegalArgumentException("Unexpected SlotKind"); + if (o instanceof Boolean) { + frame.setBoolean(slot, (boolean) o); + if (illegal) { + fd.setFrameSlotKind(slot, FrameSlotKind.Boolean); + } + } else if (o instanceof Double) { + frame.setDouble(slot, (double) o); + if (illegal) { + fd.setFrameSlotKind(slot, FrameSlotKind.Double); + } + } else if (o instanceof Long) { + frame.setLong(slot, (long) o); + if (illegal) { + fd.setFrameSlotKind(slot, FrameSlotKind.Long); + } + } else { + frame.setObject(slot, o); + if (illegal) { + fd.setFrameSlotKind(slot, FrameSlotKind.Object); + } } } } } + frame.materialize(); + return new SBlock(invokable, frame); } diff --git a/src/tools/snapshot/nodes/CachedSerializationNode.java b/src/tools/snapshot/nodes/CachedSerializationNode.java index 5e952b81f..894d286d2 100644 --- a/src/tools/snapshot/nodes/CachedSerializationNode.java +++ b/src/tools/snapshot/nodes/CachedSerializationNode.java @@ -19,7 +19,7 @@ public abstract class CachedSerializationNode extends AbstractSerializationNode private final AbstractSerializationNode cachedSerializer; public CachedSerializationNode(final Object o) { - super(null); + super(); this.guard = DispatchGuard.create(o); this.cachedSerializer = Types.getClassOf(o).getSerializer(); } diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 6321d3df8..bee3a4e20 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -16,11 +16,11 @@ import som.interpreter.actors.EventualSendNode; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.SResolver; -import som.interpreter.objectstorage.ClassFactory; import som.primitives.actors.PromisePrims; import som.vm.constants.Classes; import som.vm.constants.Nil; import som.vmobjects.SBlock; +import som.vmobjects.SClass; import som.vmobjects.SSymbol; import tools.concurrency.TracingActors.TracingActor; import tools.snapshot.SnapshotBackend; @@ -32,12 +32,12 @@ @GenerateNodeFactory public abstract class MessageSerializationNode extends AbstractSerializationNode { - public MessageSerializationNode(final ClassFactory factory) { - super(factory); + public MessageSerializationNode(final SClass clazz) { + super(clazz); } public MessageSerializationNode() { - super(Classes.messageClass.getInstanceFactory()); + super(Classes.messageClass); } protected static final int COMMONALITY_BYTES = 7; @@ -309,6 +309,7 @@ public EventualMessage deserialize(final DeserializationBuffer bb) { 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: @@ -347,6 +348,7 @@ private PromiseCallbackMessage deserializeCallback(final Actor sender, } Object[] args = parseArguments(bb); + assert args[0] != null; RootCallTarget onReceive = PromisePrims.createReceived((SBlock) args[0]); PromiseCallbackMessage pcm = new PromiseCallbackMessage(sender, (SBlock) args[0], resolver, @@ -364,11 +366,12 @@ private PromiseCallbackMessage deserializeCallback(final Actor sender, private DirectMessage deserializeDirect(final SSymbol selector, final Actor sender, final DeserializationBuffer bb, final SResolver resolver) { Object[] args = parseArguments(bb); - RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, null, + RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, + SomLanguage.getSyntheticSource("Deserialized Message", "Trace").createSection(1), SomLanguage.getLanguage(getRootNode())); DirectMessage dm = - new DirectMessage(EventualMessage.getActorCurrentMessageIsExecutionOn(), selector, + new DirectMessage(SnapshotBackend.getCurrentActor(), selector, args, sender, resolver, onReceive, false, false); @@ -390,7 +393,8 @@ private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Ac Actor finalSender = SnapshotBackend.lookupActor(bb.getInt()); Object[] args = parseArguments(bb); - RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, null, + RootCallTarget onReceive = EventualSendNode.createOnReceiveCallTarget(selector, + SomLanguage.getSyntheticSource("Deserialized Message", "Trace").createSection(1), SomLanguage.getLanguage(getRootNode())); // backup value for resolution. @@ -408,7 +412,7 @@ 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; diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index eea8f33d0..f7964fe95 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -16,16 +16,15 @@ import som.interpreter.nodes.dispatch.CachedSlotWrite; import som.interpreter.nodes.dispatch.DispatchGuard; import som.interpreter.nodes.dispatch.UninitializedDispatchNode; -import som.interpreter.objectstorage.ClassFactory; 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.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; @@ -45,12 +44,12 @@ public int compare(final SlotDefinition o1, final SlotDefinition o2) { } } - protected ObjectSerializationNode(final ClassFactory classFact) { - super(classFact); + protected ObjectSerializationNode(final SClass clazz) { + super(clazz); } - public static ObjectSerializationNode create(final ClassFactory classFact) { - return UninitializedObjectSerializationNodeFactory.create(classFact); + public static ObjectSerializationNode create(final SClass clazz) { + return UninitializedObjectSerializationNodeFactory.create(clazz); } protected final CachedSlotWrite[] createWriteNodes(final SObject o) { @@ -119,17 +118,17 @@ protected final CachedSlotRead[] createReadNodes(final SObject o) { public abstract static class UninitializedObjectSerializationNode extends ObjectSerializationNode { - protected UninitializedObjectSerializationNode(final ClassFactory classFact) { - super(classFact); + protected UninitializedObjectSerializationNode(final SClass clazz) { + super(clazz); } @Specialization public void serialize(final SObjectWithClass o, final SnapshotBuffer sb) { if (o instanceof SObject) { - replace(SObjectSerializationNodeFactory.create(classFact, + replace(SObjectSerializationNodeFactory.create(clazz, createReadNodes((SObject) o))).serialize((SObject) o, sb); } else if (o instanceof SObjectWithoutFields) { - replace(SObjectWithoutFieldsSerializationNodeFactory.create(classFact)).serialize( + replace(SObjectWithoutFieldsSerializationNodeFactory.create(clazz)).serialize( (SObjectWithoutFields) o, sb); } @@ -138,11 +137,11 @@ public void serialize(final SObjectWithClass o, final SnapshotBuffer sb) { @Override public Object deserialize(final DeserializationBuffer sb) { if (classFact.hasSlots()) { - return replace(SObjectSerializationNodeFactory.create(classFact, null)).deserialize( + return replace(SObjectSerializationNodeFactory.create(clazz)).deserialize( sb); } else { return replace( - SObjectWithoutFieldsSerializationNodeFactory.create(classFact)).deserialize(sb); + SObjectWithoutFieldsSerializationNodeFactory.create(clazz)).deserialize(sb); } } } @@ -159,14 +158,20 @@ public abstract static class SObjectSerializationNode @Children private CachedSerializationNode[] cachedSerializers; protected final ObjectLayout layout; - protected SObjectSerializationNode(final ClassFactory classFact, + protected SObjectSerializationNode(final SClass clazz, final CachedSlotRead[] reads) { - super(classFact); + super(clazz); layout = classFact.getInstanceLayout(); fieldReads = insert(reads); fieldCnt = fieldReads.length; } + protected SObjectSerializationNode(final SClass clazz) { + super(clazz); + layout = classFact.getInstanceLayout(); + fieldCnt = layout.getNumberOfFields(); + } + @Specialization public void serialize(final SObject so, final SnapshotBuffer sb) { if (!so.isLayoutCurrent()) { @@ -176,7 +181,7 @@ public void serialize(final SObject so, final SnapshotBuffer sb) { if (!layout.isValid()) { // replace this with a new node for the new layout SObjectSerializationNode replacement = - SObjectSerializationNodeFactory.create(classFact, createReadNodes(so)); + SObjectSerializationNodeFactory.create(clazz, createReadNodes(so)); replace(replacement).serialize(so, sb); } else { doCached(so, sb); @@ -194,7 +199,7 @@ protected final CachedSerializationNode[] getSerializers(final SObject o) { @ExplodeLoop public void doCached(final SObject o, final SnapshotBuffer sb) { - int base = sb.addObjectWithFields(o, classFact, fieldCnt); + int base = sb.addObjectWithFields(o, clazz, fieldCnt); if (cachedSerializers == null) { cachedSerializers = insert(getSerializers(o)); @@ -221,11 +226,11 @@ public Object deserialize(final DeserializationBuffer sb) { 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()); } @@ -235,22 +240,17 @@ public Object deserialize(final DeserializationBuffer sb) { } 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; } @@ -266,19 +266,19 @@ public void fixUp(final Object res) { public abstract static class SObjectWithoutFieldsSerializationNode extends ObjectSerializationNode { - protected SObjectWithoutFieldsSerializationNode(final ClassFactory classFact) { - super(classFact); + protected SObjectWithoutFieldsSerializationNode(final SClass clazz) { + super(clazz); } @ExplodeLoop @Specialization public void serialize(final SObjectWithoutFields o, final SnapshotBuffer sb) { - sb.addObject(o, classFact, 0); + sb.addObject(o, clazz, 0); } @Override public Object deserialize(final DeserializationBuffer sb) { - return new SObjectWithoutFields(SnapshotBackend.lookupClass(classFact.getIdentifier()), + return new SObjectWithoutFields(clazz, classFact); } } diff --git a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java index 718e1acb5..e957aaefd 100644 --- a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java +++ b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java @@ -10,12 +10,12 @@ import com.oracle.truffle.api.dsl.Specialization; 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.SSymbol; +import tools.concurrency.TracingActors.ReplayActor; import tools.concurrency.TracingActors.TracingActor; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; @@ -27,8 +27,8 @@ public abstract class PrimitiveSerializationNodes { @GenerateNodeFactory public abstract static class StringSerializationNode extends AbstractSerializationNode { - public StringSerializationNode(final ClassFactory classFact) { - super(classFact); + public StringSerializationNode(final SClass clazz) { + super(clazz); } @Specialization @@ -37,7 +37,7 @@ public void serialize(final Object o, final SnapshotBuffer sb) { 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, clazz, data.length + 4); sb.putIntAt(base, data.length); sb.putBytesAt(base + 4, data); } @@ -55,15 +55,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class IntegerSerializationNode extends AbstractSerializationNode { - public IntegerSerializationNode(final ClassFactory classFact) { - super(classFact); + public IntegerSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, Long.BYTES); sb.putLongAt(base, l); } @@ -76,15 +76,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class DoubleSerializationNode extends AbstractSerializationNode { - public DoubleSerializationNode(final ClassFactory classFact) { - super(classFact); + public DoubleSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, Double.BYTES); sb.putDoubleAt(base, d); } @@ -97,15 +97,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class BooleanSerializationNode extends AbstractSerializationNode { - public BooleanSerializationNode(final ClassFactory classFact) { - super(classFact); + public BooleanSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, 1); sb.putByteAt(base, (byte) (b ? 1 : 0)); } @@ -118,15 +118,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class TrueSerializationNode extends AbstractSerializationNode { - public TrueSerializationNode(final ClassFactory classFact) { - super(classFact); + public TrueSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, 0); } @Override @@ -138,15 +138,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class FalseSerializationNode extends AbstractSerializationNode { - public FalseSerializationNode(final ClassFactory classFact) { - super(classFact); + public FalseSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, 0); } @Override @@ -158,15 +158,15 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class SymbolSerializationNode extends AbstractSerializationNode { - public SymbolSerializationNode(final ClassFactory classFact) { - super(classFact); + public SymbolSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, 2); sb.putShortAt(base, ss.getSymbolId()); } @@ -180,41 +180,42 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class ClassSerializationNode extends AbstractSerializationNode { - public ClassSerializationNode(final ClassFactory classFact) { - super(classFact); + public ClassSerializationNode(final SClass clazz) { + super(clazz); } - protected short getSymbolId(final SClass clazz) { - return clazz.getMixinDefinition().getIdentifier().getSymbolId(); + protected int getSymbolId(final SClass clazzz) { + return clazzz.getIdentity(); } @Specialization protected void doCached(final SClass cls, final SnapshotBuffer sb, - @Cached("getSymbolId(cls)") final short cachedId) { + @Cached("getSymbolId(cls)") final int cachedId) { CompilerAsserts.compilationConstant(cachedId); - int base = sb.addObject(cls, Classes.classClass.getFactory(), 2); - sb.putShortAt(base, cachedId); + int base = sb.addObject(cls, Classes.classClass, Integer.BYTES); + sb.putIntAt(base, cachedId); } @Override public Object deserialize(final DeserializationBuffer sb) { - short id = sb.getShort(); - return SnapshotBackend.lookupClass(id); + int id = sb.getInt(); + // If lookup yields null we need to fixup in caller + return SnapshotBackend.lookupClass(id, sb.position()); } } @GenerateNodeFactory public abstract static class SInvokableSerializationNode extends AbstractSerializationNode { - public SInvokableSerializationNode(final ClassFactory classFact) { - super(classFact); + public SInvokableSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, Short.BYTES); sb.putShortAt(base, si.getIdentifier().getSymbolId()); } @@ -229,13 +230,13 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class NilSerializationNode extends AbstractSerializationNode { - public NilSerializationNode(final ClassFactory classFact) { - super(classFact); + public NilSerializationNode(final SClass clazz) { + super(clazz); } @Specialization public void serialize(final Object o, final SnapshotBuffer sb) { - sb.addObject(o, classFact, 0); + sb.addObject(o, clazz, 0); } @Override @@ -247,13 +248,13 @@ public Object deserialize(final DeserializationBuffer sb) { @GenerateNodeFactory public abstract static class FarRefSerializationNode extends AbstractSerializationNode { - public FarRefSerializationNode(final ClassFactory classFact) { - super(classFact); + public FarRefSerializationNode(final SClass clazz) { + super(clazz); } @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, clazz, Integer.BYTES + Long.BYTES); TracingActor other = (TracingActor) o.getActor(); sb.putIntAt(base, other.getActorId()); @@ -265,14 +266,18 @@ 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); + } - Object value = otherDB.getReference(); + Object value = sb.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..4a2ed207b 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -6,13 +6,15 @@ import com.oracle.truffle.api.dsl.Specialization; import som.interpreter.Types; -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.objectstorage.ClassFactory; import tools.concurrency.TracingActors.TracingActor; +import som.interpreter.objectstorage.ClassFactory; +import som.interpreter.actors.SPromise.STracingPromise; +import som.vmobjects.SClass; +import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; @@ -23,13 +25,13 @@ public abstract class PromiseSerializationNodes { @GenerateNodeFactory public abstract static class PromiseSerializationNode extends AbstractSerializationNode { - public PromiseSerializationNode(final ClassFactory classFact) { - super(classFact); + public PromiseSerializationNode(final SClass clazz) { + super(clazz); } @Specialization(guards = "prom.isCompleted()") public void doResolved(final SPromise prom, final SnapshotBuffer sb) { - int base = sb.addObject(prom, classFact, 1 + Long.BYTES); + int base = sb.addObject(prom, clazz, 1 + Long.BYTES + Integer.BYTES); // resolutionstate switch (prom.getResolutionStateUnsync()) { @@ -48,6 +50,7 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { Types.getClassOf(value).serialize(value, sb); } sb.putLongAt(base + 1, sb.getRecord().getObjectPointer(value)); + sb.putIntAt(base + 1 + Long.BYTES, ((STracingPromise) prom).getResolvingActor()); } @Specialization(guards = "!prom.isCompleted()") @@ -74,7 +77,7 @@ 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, clazz, 1 + 6 + Long.BYTES * (noe + nwr + ncp)); // resolutionstate sb.putByteAt(base, (byte) 0); @@ -169,10 +172,12 @@ public SPromise deserialize(final DeserializationBuffer sb) { private SPromise deserializeCompletedPromise(final byte state, final DeserializationBuffer sb) { Object value = sb.getReference(); + int resolver = sb.getInt(); SPromise p = SPromise.createResolved( - EventualMessage.getActorCurrentMessageIsExecutionOn(), value, + SnapshotBackend.getCurrentActor(), value, state == 1 ? Resolution.SUCCESSFUL : Resolution.ERRONEOUS); + ((STracingPromise) p).setResolvingActorForSnapshot(resolver); if (DeserializationBuffer.needsFixup(value)) { sb.installFixup(new PromiseValueFixup(p)); @@ -183,7 +188,7 @@ private SPromise deserializeCompletedPromise(final byte state, private SPromise deserializeUnresolvedPromise(final DeserializationBuffer sb) { SPromise promise = SPromise.createPromise( - EventualMessage.getActorCurrentMessageIsExecutionOn(), false, false, null); + SnapshotBackend.getCurrentActor(), false, false, null); // These messages aren't referenced by anything else, no need for fixup int whenResolvedCnt = sb.getShort(); @@ -231,13 +236,13 @@ public void fixUp(final Object o) { @GenerateNodeFactory public abstract static class ResolverSerializationNode extends AbstractSerializationNode { - public ResolverSerializationNode(final ClassFactory classFact) { - super(classFact); + public ResolverSerializationNode(final SClass clazz) { + super(clazz); } @Specialization public void doResolver(final SResolver resolver, final SnapshotBuffer sb) { - int base = sb.addObject(resolver, classFact, Long.BYTES); + int base = sb.addObject(resolver, clazz, Long.BYTES); SPromise prom = resolver.getPromise(); if (prom.getOwner() == sb.getOwner().getCurrentActor()) { if (!sb.getRecord().containsObject(prom)) { From 1e47a14e54fc4dc1f7395cd9dda404d37554c49b Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 13 Dec 2018 14:50:36 +0100 Subject: [PATCH 02/80] revise promise serialization --- .../interpreter/actors/EventualMessage.java | 5 + src/som/interpreter/actors/SPromise.java | 2 +- .../concurrency/TracingActivityThread.java | 2 +- src/tools/snapshot/SnapshotBackend.java | 1 - .../DeserializationBuffer.java | 2 +- .../nodes/PromiseSerializationNodes.java | 96 ++++++++++++++----- 6 files changed, 80 insertions(+), 28 deletions(-) diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index cffee1e3f..daafe3bbf 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -91,6 +91,11 @@ public long serialize(final SnapshotBuffer sb) { } } + public long forceSerialize(final SnapshotBuffer sb) { + ReceivedRootNode rm = (ReceivedRootNode) this.onReceive.getRootNode(); + return rm.getSerializer().execute(this, sb); + } + /** * A message to a known receiver that is to be executed on the actor owning * the receiver. diff --git a/src/som/interpreter/actors/SPromise.java b/src/som/interpreter/actors/SPromise.java index 3bd7f0cb9..a22f1348a 100644 --- a/src/som/interpreter/actors/SPromise.java +++ b/src/som/interpreter/actors/SPromise.java @@ -471,7 +471,7 @@ protected static void resolveAndTriggerListenersUnsynced(final Resolution type, // because after resolving the promise, all clients will schedule their // callbacks/msg themselves synchronized (p) { - assert p.assertNotCompleted(); + // assert p.assertNotCompleted(); // TODO: is this correct? can we just resolve chained promises like this? this means, // their state changes twice. I guess it is ok, not sure about synchronization // thought. They are created as 'chained', and then there is the resolute propagation diff --git a/src/tools/concurrency/TracingActivityThread.java b/src/tools/concurrency/TracingActivityThread.java index 7e99872f6..d9e11b9d5 100644 --- a/src/tools/concurrency/TracingActivityThread.java +++ b/src/tools/concurrency/TracingActivityThread.java @@ -225,7 +225,7 @@ public byte getSnapshotId() { private void newSnapshot() { TracingActor ta = (TracingActor) ((ActorProcessingThread) this).getCurrentActor(); - TraceActorContextNode tracer = ta.getActorContextNode();// get from ta? + TraceActorContextNode tracer = ta.getActorContextNode(); traceBuffer.swapStorage(); if (tracer != null) { tracer.trace(ta); diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index 912b00d16..6e2dc36df 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -171,7 +171,6 @@ private static SClass createSClass(final int id) { LinkedList todo = (LinkedList) current; DeserializationBuffer db = SnapshotParser.getDeserializationBuffer(); for (long ref : todo) { - System.out.println("fixing" + result); db.fixUpIfNecessary(ref, result); } } diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index ea2ecaa1e..8bbd92289 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -250,7 +250,7 @@ public void position(final long newPosition) { // cut away the thread identification // 0x FF FF FF FF FF FF - long offset = 0x0000FFFFFFFFFFFFl & newPosition; + long offset = 0x0000FFFFFFFFFFFFL & newPosition; // absolute position in file if (!VmSettings.TEST_SNAPSHOTS) { diff --git a/src/tools/snapshot/nodes/PromiseSerializationNodes.java b/src/tools/snapshot/nodes/PromiseSerializationNodes.java index 4a2ed207b..1f3402b7a 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -10,10 +10,9 @@ import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.Resolution; import som.interpreter.actors.SPromise.SResolver; -import tools.concurrency.TracingActors.TracingActor; -import som.interpreter.objectstorage.ClassFactory; import som.interpreter.actors.SPromise.STracingPromise; import som.vmobjects.SClass; +import tools.concurrency.TracingActors.TracingActor; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; @@ -22,6 +21,24 @@ public abstract class PromiseSerializationNodes { + private static void handleReferencedPromise(final SPromise prom, + final SnapshotBuffer sb, final int location) { + if (prom.getOwner() == sb.getOwner().getCurrentActor()) { + if (!sb.getRecord().containsObject(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 { @@ -31,7 +48,30 @@ public PromiseSerializationNode(final SClass clazz) { @Specialization(guards = "prom.isCompleted()") public void doResolved(final SPromise prom, final SnapshotBuffer sb) { - int base = sb.addObject(prom, clazz, 1 + Long.BYTES + Integer.BYTES); + 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); + + whenRes = prom.getWhenResolvedUnsync(); + whenResExt = prom.getWhenResolvedExtUnsync(); + nwr = getObjectCnt(whenRes, whenResExt); + + onError = prom.getOnError(); + onErrorExt = prom.getOnErrorExtUnsync(); + noe = getObjectCnt(onError, onErrorExt); + } + int base = sb.addObject(prom, clazz, + 1 + 6 + Integer.BYTES + Long.BYTES * (noe + nwr + ncp + 1)); // resolutionstate switch (prom.getResolutionStateUnsync()) { @@ -51,6 +91,10 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { } sb.putLongAt(base + 1, sb.getRecord().getObjectPointer(value)); sb.putIntAt(base + 1 + Long.BYTES, ((STracingPromise) prom).getResolvingActor()); + base += (1 + Integer.BYTES + Long.BYTES); + base = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); + base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); + serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); } @Specialization(guards = "!prom.isCompleted()") @@ -104,12 +148,12 @@ private int serializeWhenResolvedMsgs(final int start, final int cnt, sb.putShortAt(base, (short) cnt); base += 2; if (cnt > 0) { - sb.putLongAt(base, whenRes.serialize(sb)); + 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)); + sb.putLongAt(base + i * Long.BYTES, whenResExt.get(i).forceSerialize(sb)); } base += whenResExt.size() * Long.BYTES; } @@ -124,12 +168,12 @@ private int serializeOnErrorMsgs(final int start, final int cnt, sb.putShortAt(base, (short) cnt); base += 2; if (cnt > 0) { - sb.putLongAt(base, onError.serialize(sb)); + sb.putLongAt(base, onError.forceSerialize(sb)); base += Long.BYTES; if (cnt > 1) { for (int i = 0; i < onErrorExt.size(); i++) { - sb.putLongAt(base + i * Long.BYTES, onErrorExt.get(i).serialize(sb)); + sb.putLongAt(base + i * Long.BYTES, onErrorExt.get(i).forceSerialize(sb)); } base += onErrorExt.size() * Long.BYTES; } @@ -150,9 +194,8 @@ private void serializeChainedPromises(final int start, final int cnt, 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); } } } @@ -183,6 +226,24 @@ private SPromise deserializeCompletedPromise(final byte state, 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++) { + SPromise remote = (SPromise) sb.getReference(); + p.addChainedPromise(remote); + } + return p; } @@ -244,20 +305,7 @@ public ResolverSerializationNode(final SClass clazz) { public void doResolver(final SResolver resolver, final SnapshotBuffer sb) { int base = sb.addObject(resolver, clazz, 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 From c4f646bff1e886c55e2b7e81f16457aa37b0e1ef Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 13 Dec 2018 14:56:43 +0100 Subject: [PATCH 03/80] fix frame identity --- src/som/vm/ObjectSystem.java | 3 + src/som/vm/constants/Classes.java | 2 + src/som/vmobjects/SInvokable.java | 18 +- .../DeserializationBuffer.java | 31 +- .../nodes/BlockSerializationNode.java | 299 ++++++++++-------- 5 files changed, 222 insertions(+), 131 deletions(-) diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index 5365c232a..ccdf4d504 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -56,6 +56,7 @@ import tools.snapshot.nodes.BlockSerializationNodeFactory; import tools.snapshot.nodes.MessageSerializationNodeFactory; import tools.snapshot.nodes.ObjectSerializationNodesFactory.SObjectWithoutFieldsSerializationNodeFactory; +import tools.snapshot.nodes.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory; import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.BooleanSerializationNodeFactory; import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.ClassSerializationNodeFactory; import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.DoubleSerializationNodeFactory; @@ -427,6 +428,8 @@ public SObjectWithoutFields initialize() { // these classes are not exposed in Newspeak directly, and thus, do not yet have a class // factory setDummyClassFactory(Classes.messageClass, MessageSerializationNodeFactory.getInstance()); + setDummyClassFactory(Classes.frameClass, + UninitializedObjectSerializationNodeFactory.getInstance()); setDummyClassFactory(Classes.methodClass, SInvokableSerializationNodeFactory.getInstance()); 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/SInvokable.java b/src/som/vmobjects/SInvokable.java index 869b723c4..0d9f95a57 100644 --- a/src/som/vmobjects/SInvokable.java +++ b/src/som/vmobjects/SInvokable.java @@ -34,12 +34,14 @@ 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 +50,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 +62,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 +187,15 @@ public final SourceSection getSourceSection() { return invokable.getSourceSection(); } + public FrameSerializationNode getFrameSerializer() { + if (frameSerializer == null) { + 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) { diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index 8bbd92289..26cebc7db 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -10,6 +10,7 @@ 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; @@ -126,9 +127,7 @@ public Object getReference() { long current = position(); printPosition(reference); - if (!deserialized.containsKey(reference)) { - deserialized.put(reference, null); - } + deserialized.put(reference, null); // prepare deserialize referenced object position(reference); @@ -150,6 +149,32 @@ public Object getReference() { } } + 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(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); + return o; + } else { + return deserialized.get(reference); + } + } + public Object getReference(final long location) { return deserialized.get(location); } diff --git a/src/tools/snapshot/nodes/BlockSerializationNode.java b/src/tools/snapshot/nodes/BlockSerializationNode.java index 423b208bb..15909feb8 100644 --- a/src/tools/snapshot/nodes/BlockSerializationNode.java +++ b/src/tools/snapshot/nodes/BlockSerializationNode.java @@ -1,5 +1,8 @@ package tools.snapshot.nodes; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -10,14 +13,12 @@ import som.compiler.Variable.Internal; import som.interpreter.FrameOnStackMarker; -import som.interpreter.Method; import som.interpreter.Types; import som.vm.constants.Classes; import som.vmobjects.SAbstractObject; import som.vmobjects.SBlock; import som.vmobjects.SClass; import som.vmobjects.SInvokable; -import som.vmobjects.SSymbol; import tools.snapshot.SnapshotBackend; import tools.snapshot.SnapshotBuffer; import tools.snapshot.SnapshotRecord; @@ -41,31 +42,121 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { MaterializedFrame mf = block.getContextOrNull(); if (mf == null) { - int base = sb.addObject(block, clazz, SINVOKABLE_SIZE + 2); + int base = sb.addObject(block, clazz, SINVOKABLE_SIZE + 1); SInvokable meth = block.getMethod(); - // System.out.println("Block without frame " + meth.getIdentifier()); 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, clazz, - SINVOKABLE_SIZE + ((args.length + fd.getSlots().size()) * Long.BYTES) + 2); - int base = start; + int base = sb.addObject(block, clazz, SINVOKABLE_SIZE + 1 + Long.BYTES); SInvokable meth = block.getMethod(); - // System.out.println("Block " + meth.getIdentifier()); sb.putShortAt(base, meth.getIdentifier().getSymbolId()); - // System.out.println( - // "with " + args.length + " args at: " + base + " in " + sb.getOwner().getThreadId()); - // for (Object o : args) { - // System.out.println("\t" + o); - // } + sb.putByteAt(base + 2, (byte) 1); + + if (!sb.getRecord().containsObject(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"; - sb.putByteAt(base + 2, (byte) args.length); - base += 3; + 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) { + super(Classes.frameClass); + this.frameDescriptor = frameDescriptor; + } + + // 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(); + int slotCnt = frameDescriptor.getSlots().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++) { @@ -78,17 +169,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 : frameDescriptor.getSlots()) { // 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); - // System.out.println("Slot" + slot + value); - switch (fd.getFrameSlotKind(slot)) { + Object value = frame.getValue(slot); + switch (frameDescriptor.getFrameSlotKind(slot)) { case Boolean: Classes.booleanClass.serialize(value, sb); break; @@ -110,7 +201,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"); @@ -122,122 +214,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(); - SSymbol inv = SnapshotBackend.getSymbolForId(sinv); - - SInvokable invokable = SnapshotBackend.lookupInvokable(sinv); - assert invokable != null : "Invokable not found"; - FrameDescriptor fd = ((Method) invokable.getInvokable()).getLexicalScope().getOuterMethod() - .getMethod().getFrameDescriptor(); - // TODO Nullpointer when getting FD - - // read num args - int numArgs = bb.get(); - Object[] args = new Object[numArgs]; - - // System.out.println("Block with " + inv); - // 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; + } } - } - - // System.out.println(Arrays.toString(args)); - 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 (slot.getIdentifier() instanceof Internal) { - FrameOnStackMarker fosm = new FrameOnStackMarker(); - if (!(boolean) o) { - fosm.frameNoLongerOnStack(); + 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; } - o = fosm; - } - - boolean illegal = fd.getFrameSlotKind(slot) == FrameSlotKind.Illegal; - if (o instanceof Boolean) { - frame.setBoolean(slot, (boolean) o); - if (illegal) { - fd.setFrameSlotKind(slot, FrameSlotKind.Boolean); - } - } else if (o instanceof Double) { - frame.setDouble(slot, (double) o); - if (illegal) { - fd.setFrameSlotKind(slot, FrameSlotKind.Double); - } - } else if (o instanceof Long) { - frame.setLong(slot, (long) o); - if (illegal) { - fd.setFrameSlotKind(slot, FrameSlotKind.Long); - } - } else { - frame.setObject(slot, o); - if (illegal) { - fd.setFrameSlotKind(slot, FrameSlotKind.Object); + boolean illegal = frameDescriptor.getFrameSlotKind(slot) == FrameSlotKind.Illegal; + + if (o instanceof Boolean) { + frame.setBoolean(slot, (boolean) o); + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Boolean); + } + } else if (o instanceof Double) { + frame.setDouble(slot, (double) o); + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Double); + } + } else if (o instanceof Long) { + frame.setLong(slot, (long) o); + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Long); + } + } else { + frame.setObject(slot, o); + if (illegal) { + frameDescriptor.setFrameSlotKind(slot, FrameSlotKind.Object); + } } } } } - } - - frame.materialize(); - 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; } } } From ea7e57fb2659e2c26629fe8d5e6b74ce42c59848 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 13 Dec 2018 16:50:40 +0100 Subject: [PATCH 04/80] fix synchronisation issue in SnapshotRecord --- src/som/primitives/SystemPrims.java | 2 +- src/som/vmobjects/SClass.java | 2 +- src/tools/snapshot/SnapshotBackend.java | 2 +- src/tools/snapshot/SnapshotBuffer.java | 4 ++-- src/tools/snapshot/SnapshotRecord.java | 13 +++++++++++-- .../snapshot/nodes/BlockSerializationNode.java | 2 +- .../snapshot/nodes/MessageSerializationNode.java | 4 ++-- .../snapshot/nodes/ObjectSerializationNodes.java | 2 +- .../snapshot/nodes/PromiseSerializationNodes.java | 4 ++-- 9 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/som/primitives/SystemPrims.java b/src/som/primitives/SystemPrims.java index eae144dd3..312739607 100644 --- a/src/som/primitives/SystemPrims.java +++ b/src/som/primitives/SystemPrims.java @@ -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(); diff --git a/src/som/vmobjects/SClass.java b/src/som/vmobjects/SClass.java index f1a105937..d56952be4 100644 --- a/src/som/vmobjects/SClass.java +++ b/src/som/vmobjects/SClass.java @@ -377,7 +377,7 @@ public MaterializedFrame getContext() { public void serialize(final Object o, final SnapshotBuffer sb) { assert instanceClassGroup != null; - if (!sb.getRecord().containsObject(o)) { + if (!sb.getRecord().containsObjectUnsync(o)) { getSerializer().execute(o, sb); } } diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index 6e2dc36df..f415adb3b 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -354,7 +354,7 @@ private static int writeClassEnclosures(final FileOutputStream fos) throws IOExc // TODO if necessary keep actual ownership information, for now we assume all the class // creation is happening in the main actor. TracingActor owner = (TracingActor) vm.getMainActor(); - if (!owner.getSnapshotRecord().containsObject(outer)) { + if (!owner.getSnapshotRecord().containsObjectUnsync(outer)) { // just use the first buffer available; that object isn't used anywhere else outer.getSOMClass().serialize(outer, buffers.peek()); } diff --git a/src/tools/snapshot/SnapshotBuffer.java b/src/tools/snapshot/SnapshotBuffer.java index 0a7238630..c00b79f8f 100644 --- a/src/tools/snapshot/SnapshotBuffer.java +++ b/src/tools/snapshot/SnapshotBuffer.java @@ -39,7 +39,7 @@ public final long calculateReference(final long start) { } public int addObject(final Object o, final SClass clazz, final int payload) { - assert !getRecord().containsObject(o) : "Object serialized multiple times"; + assert !getRecord().containsObjectUnsync(o) : "Object serialized multiple times"; int oldPos = this.position; getRecord().addObjectEntry(o, calculateReference(oldPos)); @@ -52,7 +52,7 @@ public int addObject(final Object o, final SClass clazz, final int payload) { public int addObjectWithFields(final Object o, final SClass clazz, final int fieldCnt) { assert fieldCnt < MAX_FIELD_CNT; - assert !getRecord().containsObject(o) : "Object serialized multiple times"; + assert !getRecord().containsObjectUnsync(o) : "Object serialized multiple times"; int oldPos = this.position; getRecord().addObjectEntry(o, calculateReference(oldPos)); diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 309c39329..fb5aaa812 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -30,10 +30,19 @@ public SnapshotRecord() { this.externalReferences = new ConcurrentLinkedQueue<>(); } - public boolean containsObject(final Object o) { + /** + * only use this in the actor that owns this record (only the owner adds entries). + */ + public boolean containsObjectUnsync(final Object o) { return entries.containsKey(o); } + public boolean containsObject(final Object o) { + synchronized (entries) { + return entries.containsKey(o); + } + } + public long getObjectPointer(final Object o) { if (entries.containsKey(o)) { return entries.get(o); @@ -54,7 +63,7 @@ public void handleTodos(final SnapshotBuffer sb) { // ignore todos from a different snapshot if (frt.referer.snapshotVersion == sb.snapshotVersion) { - if (!this.containsObject(frt.target)) { + if (!this.containsObjectUnsync(frt.target)) { Types.getClassOf(frt.target).serialize(frt.target, sb); } diff --git a/src/tools/snapshot/nodes/BlockSerializationNode.java b/src/tools/snapshot/nodes/BlockSerializationNode.java index 15909feb8..b6356b7d9 100644 --- a/src/tools/snapshot/nodes/BlockSerializationNode.java +++ b/src/tools/snapshot/nodes/BlockSerializationNode.java @@ -53,7 +53,7 @@ public void serialize(final SBlock block, final SnapshotBuffer sb) { sb.putShortAt(base, meth.getIdentifier().getSymbolId()); sb.putByteAt(base + 2, (byte) 1); - if (!sb.getRecord().containsObject(mf)) { + if (!sb.getRecord().containsObjectUnsync(mf)) { meth.getFrameSerializer().execute(block, sb); } sb.putLongAt(base + 3, sb.getRecord().getObjectPointer(mf)); diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index bee3a4e20..f7a9148c7 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -82,13 +82,13 @@ protected final void doArguments(final Object[] args, final int base, sb.putByteAt(base, (byte) args.length); for (int i = 0; i < args.length; i++) { if (args[i] == null) { - if (!sb.getRecord().containsObject(Nil.nilObject)) { + if (!sb.getRecord().containsObjectUnsync(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])) { + if (!sb.getRecord().containsObjectUnsync(args[i])) { Types.getClassOf(args[i]).serialize(args[i], sb); } sb.putLongAt((base + 1) + i * Long.BYTES, sb.getRecord().getObjectPointer(args[i])); diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index f7964fe95..d1dd53838 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -211,7 +211,7 @@ public void doCached(final SObject o, final SnapshotBuffer sb) { // 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 (!sb.getRecord().containsObjectUnsync(value)) { // Referenced Object not yet in snapshot cachedSerializers[i].serialize(value, sb); } diff --git a/src/tools/snapshot/nodes/PromiseSerializationNodes.java b/src/tools/snapshot/nodes/PromiseSerializationNodes.java index 1f3402b7a..13139c832 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -24,7 +24,7 @@ public abstract class PromiseSerializationNodes { private static void handleReferencedPromise(final SPromise prom, final SnapshotBuffer sb, final int location) { if (prom.getOwner() == sb.getOwner().getCurrentActor()) { - if (!sb.getRecord().containsObject(prom)) { + if (!sb.getRecord().containsObjectUnsync(prom)) { SPromise.getPromiseClass().serialize(prom, sb); } sb.putLongAt(location, sb.getRecord().getObjectPointer(prom)); @@ -86,7 +86,7 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { } Object value = prom.getValueForSnapshot(); - if (!sb.getRecord().containsObject(value)) { + if (!sb.getRecord().containsObjectUnsync(value)) { Types.getClassOf(value).serialize(value, sb); } sb.putLongAt(base + 1, sb.getRecord().getObjectPointer(value)); From 8dff05271705a26795a7348050f5fe19bb7bc63d Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Fri, 14 Dec 2018 11:00:32 +0100 Subject: [PATCH 05/80] fix promise ownership --- .../nodes/PromiseSerializationNodes.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/tools/snapshot/nodes/PromiseSerializationNodes.java b/src/tools/snapshot/nodes/PromiseSerializationNodes.java index 13139c832..8d4bfa2a9 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.Specialization; import som.interpreter.Types; +import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.Resolution; @@ -71,7 +72,7 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { noe = getObjectCnt(onError, onErrorExt); } int base = sb.addObject(prom, clazz, - 1 + 6 + Integer.BYTES + Long.BYTES * (noe + nwr + ncp + 1)); + 1 + 6 + Integer.BYTES + Integer.BYTES + Long.BYTES * (noe + nwr + ncp + 1)); // resolutionstate switch (prom.getResolutionStateUnsync()) { @@ -89,9 +90,11 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { if (!sb.getRecord().containsObjectUnsync(value)) { Types.getClassOf(value).serialize(value, sb); } - sb.putLongAt(base + 1, sb.getRecord().getObjectPointer(value)); - sb.putIntAt(base + 1 + Long.BYTES, ((STracingPromise) prom).getResolvingActor()); - base += (1 + Integer.BYTES + Long.BYTES); + 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 = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); @@ -121,11 +124,13 @@ public void doUnresolved(final SPromise prom, final SnapshotBuffer sb) { onErrorExt = prom.getOnErrorExtUnsync(); noe = getObjectCnt(onError, onErrorExt); } - int base = sb.addObject(prom, clazz, 1 + 6 + Long.BYTES * (noe + nwr + ncp)); + int base = + sb.addObject(prom, clazz, 1 + Integer.BYTES + 6 + Long.BYTES * (noe + nwr + ncp)); // resolutionstate sb.putByteAt(base, (byte) 0); - base++; + sb.putIntAt(base + 1, ((TracingActor) prom.getOwner()).getActorId()); + base += 1 + Integer.BYTES; base = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); @@ -214,11 +219,12 @@ 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( - SnapshotBackend.getCurrentActor(), value, + SPromise p = SPromise.createResolved(owner, value, state == 1 ? Resolution.SUCCESSFUL : Resolution.ERRONEOUS); ((STracingPromise) p).setResolvingActorForSnapshot(resolver); @@ -248,8 +254,9 @@ private SPromise deserializeCompletedPromise(final byte state, } private SPromise deserializeUnresolvedPromise(final DeserializationBuffer sb) { - SPromise promise = SPromise.createPromise( - SnapshotBackend.getCurrentActor(), 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(); From 4d661647edabb8da6b8186556497274323628c3d Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Fri, 14 Dec 2018 11:01:09 +0100 Subject: [PATCH 06/80] make counting work --- core-lib/Benchmarks/SavinaSnap.ns | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core-lib/Benchmarks/SavinaSnap.ns b/core-lib/Benchmarks/SavinaSnap.ns index 6c90fa74e..1c21e4ebd 100644 --- a/core-lib/Benchmarks/SavinaSnap.ns +++ b/core-lib/Benchmarks/SavinaSnap.ns @@ -81,8 +81,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( public ping = ( pong <-: ping: self. (*('ping: ' + pingsLeft) println.*) - pingsLeft = 2500 ifTrue:[ - 'snapshot' println. + pingsLeft = (NumPings / 2) ifTrue:[ actors snapshot: self. ]. pingsLeft:: pingsLeft - 1. @@ -143,6 +142,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( ) public count: cnt = ( + counter <-: dummy. completionRes resolve: cnt = limit ) ) @@ -151,12 +151,19 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | 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 = ( From a58740f0f0c962b0cbc3cd14bdf11c95ddfec0c5 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Fri, 14 Dec 2018 11:16:17 +0100 Subject: [PATCH 07/80] fix empty array --- src/tools/snapshot/nodes/AbstractArraySerializationNode.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java index fe66832e4..326a26c17 100644 --- a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java +++ b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java @@ -155,6 +155,7 @@ protected Object parseBackingStorage(final DeserializationBuffer sb) { backing = oa; break; case TYPE_EMPTY: + backing = sb.getInt(); break; default: throw new IllegalArgumentException(); @@ -172,6 +173,9 @@ public ArraySerializationNode(final SClass clazz) { @Override public Object deserialize(final DeserializationBuffer sb) { Object backing = parseBackingStorage(sb); + if (backing == null) { + + } return new SArray.SMutableArray(backing, Classes.arrayClass); } } From 59e2dc79091927c123b1ba386776c903678adfa9 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Fri, 14 Dec 2018 12:14:04 +0100 Subject: [PATCH 08/80] fix nullpointer when actor is not in trace --- src/tools/concurrency/TraceParser.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tools/concurrency/TraceParser.java b/src/tools/concurrency/TraceParser.java index be634a826..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; @@ -88,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) { From b903569657bd08e8bbeaf12bb4c1da7ec6aeaca2 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Mon, 17 Dec 2018 09:28:19 +0100 Subject: [PATCH 09/80] fix promise message arg0 --- src/som/interpreter/actors/EventualMessage.java | 2 +- .../snapshot/nodes/MessageSerializationNode.java | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index daafe3bbf..b7c15ad1c 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -350,7 +350,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; } diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index f7a9148c7..2ac7d40c1 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -5,6 +5,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import som.Output; import som.interpreter.SomLanguage; import som.interpreter.Types; import som.interpreter.actors.Actor; @@ -200,8 +201,6 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe SPromise prom = dm.getPromise(); TracingActor fsender = (TracingActor) dm.getFinalSender(); 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); @@ -220,7 +219,6 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES; doArguments(args, base, sb); - args[0] = receiver; return sb.calculateReference(start); } @@ -400,10 +398,11 @@ private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Ac // 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; } PromiseSendMessage psm = @@ -479,7 +478,10 @@ public void setMessage(final PromiseMessage pm) { @Override public void fixUp(final Object o) { - assert pm != null; + if (o == null) { + Output.println("Test"); + } + assert pm != null && o != null; this.pm.setPromise((SPromise) o); } } From c16ebb569c3d07c425002e07b9a8777a0c9a550c Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Mon, 17 Dec 2018 09:34:27 +0100 Subject: [PATCH 10/80] fix owner and deserialization --- .../snapshot/deserialization/DeserializationBuffer.java | 6 +++++- src/tools/snapshot/deserialization/SnapshotParser.java | 4 ++++ src/tools/snapshot/nodes/ObjectSerializationNodes.java | 2 ++ src/tools/snapshot/nodes/PrimitiveSerializationNodes.java | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index 26cebc7db..0525d8f86 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -204,8 +204,12 @@ public synchronized void fixUpIfNecessary(final long reference, final Object res } } - public synchronized void installObjectFixup(final SObject o, final CachedSlotWrite write) { + public synchronized void putObject(final SObject o) { + fixUpIfNecessary(lastRef, o); deserialized.put(lastRef, o); + } + + public synchronized void installObjectFixup(final SObject o, final CachedSlotWrite write) { long backup = lastRef; long reference = getLong(); long current = position(); diff --git a/src/tools/snapshot/deserialization/SnapshotParser.java b/src/tools/snapshot/deserialization/SnapshotParser.java index d6b530c19..4207b6729 100644 --- a/src/tools/snapshot/deserialization/SnapshotParser.java +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -231,6 +231,10 @@ public static ReplayActor getCurrentActor() { return parser.currentActor; } + public static void setCurrentActor(final ReplayActor current) { + parser.currentActor = current; + } + public static SPromise getResultPromise() { assert parser.resultPromise != null; return parser.resultPromise; diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index d1dd53838..228d11b1a 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -239,6 +239,8 @@ public Object deserialize(final DeserializationBuffer sb) { fieldWrites = insert(createWriteNodes(o)); } + sb.putObject(o); + for (int i = 0; i < fieldWrites.length; i++) { sb.installObjectFixup(o, fieldWrites[i]); } diff --git a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java index e957aaefd..ff983b6de 100644 --- a/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java +++ b/src/tools/snapshot/nodes/PrimitiveSerializationNodes.java @@ -21,6 +21,7 @@ import tools.snapshot.SnapshotBuffer; import tools.snapshot.deserialization.DeserializationBuffer; import tools.snapshot.deserialization.FixupInformation; +import tools.snapshot.deserialization.SnapshotParser; public abstract class PrimitiveSerializationNodes { @@ -273,7 +274,11 @@ public Object deserialize(final DeserializationBuffer sb) { other = ReplayActor.getActorWithId(actorId); } + TracingActor current = SnapshotBackend.getCurrentActor(); + SnapshotParser.setCurrentActor((ReplayActor) other); Object value = sb.getReference(); + SnapshotParser.setCurrentActor((ReplayActor) current); + SFarReference result = new SFarReference(other, value); if (DeserializationBuffer.needsFixup(value)) { From 6ebcbb5c6ac9c87e7e104469d5b510fb5c0edd43 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Mon, 17 Dec 2018 12:09:08 +0100 Subject: [PATCH 11/80] SavinaSnap --- core-lib/Benchmarks/SavinaSnap.ns | 67 ++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/core-lib/Benchmarks/SavinaSnap.ns b/core-lib/Benchmarks/SavinaSnap.ns index 1c21e4ebd..e66984154 100644 --- a/core-lib/Benchmarks/SavinaSnap.ns +++ b/core-lib/Benchmarks/SavinaSnap.ns @@ -226,8 +226,12 @@ class Savina usingPlatform: platform andHarness: harness = Value ( (actors createActorFromValue: ThroughputActor) <-: new: promisePair resolver ]. - numMessages timesRepeat: [ - benchActors do: [:a | a <-: process ] ]. + 1 to: numMessages do:[ :i| + i = (numMessages / 2) ifTrue:[ + actors snapshot: self. + ]. + benchActors do: [:a | a <-: process ] + ]. ^ promiseGroup ) @@ -267,8 +271,11 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | promiseGroup | promiseGroup:: nil. - numActors timesRepeat: [ + 1 to: numActors do:[ :i| | promisePair | + i = (numActors / 2) ifTrue:[ + actors snapshot: self. + ]. promisePair:: actors createPromisePair. promiseGroup ifNil: [ promiseGroup:: promisePair promise ] @@ -301,6 +308,10 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | )( public ping: t = ( + t = (numPings / 2) ifTrue:[ + actors snapshot: self. + ]. + t > 0 ifTrue: [ nextAct <-: ping: t - 1 ] ifFalse: [ @@ -422,6 +433,9 @@ class Savina usingPlatform: platform andHarness: harness = Value ( waitingChameneo ifNil: [ waitingChameneo:: sender ] ifNotNil: [ + n = (numMeetings / 2) ifTrue:[ + actors snapshot: self. + ]. n:: n - 1. waitingChameneo <-: meet: sender color: color. waitingChameneo:: nil. @@ -552,9 +566,11 @@ class Savina usingPlatform: platform andHarness: harness = Value ( unwrapPromise whenResolved: [:r | sinkActor <-: neighbors: bigActors. bigActors do: [:n | n <-: neighbors: bigActors ]. - bigActors do: [:n | n <-: pong: -1 ] + bigActors do: [:n | n <-: pong: -1 ]. + actors snapshot: self. ]. + ^ completionPP promise ) @@ -596,7 +612,9 @@ class Savina usingPlatform: platform andHarness: harness = Value ( worker:: (actors createActorFromValue: Worker) <-: new: self dict: dictionary id: i. workers at: i put: worker. worker <-: doWork. - ] + ]. + + actors snapshot: self. ) public endWork = ( @@ -697,7 +715,9 @@ class Savina usingPlatform: platform andHarness: harness = Value ( 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. @@ -892,7 +912,9 @@ class Savina usingPlatform: platform andHarness: harness = Value ( 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. @@ -1109,6 +1131,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( philosophers at: i put: ph ]. philosophers do: [:ph | ph <-: start ]. + actors snapshot: self. ^ completionPP promise ) @@ -1201,7 +1224,10 @@ class Savina usingPlatform: platform andHarness: harness = Value ( private idGenerator ::= 0. |)( public start = ( - numHaircuts timesRepeat: [ + 1 to: numHaircuts do:[ :i | + i = (numHaircuts / 2) ifTrue: [ + actors snapshot: self. + ]. sendCustomerToRoom. busyWait: (random next: avHaircutRate) + 10 ]. ) @@ -1304,6 +1330,10 @@ class Savina usingPlatform: platform andHarness: harness = Value ( public startedSmoking = ( roundsSoFar:: roundsSoFar + 1. + roundsSoFar = (numRounds / 2) ifTrue: [ + actors snapshot: self. + ]. + roundsSoFar >= numRounds ifTrue: [ requestSmokersToExit ] ifFalse: [ notifyRandomSmoker ] @@ -1421,6 +1451,9 @@ class Savina usingPlatform: platform andHarness: harness = Value ( 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. *) @@ -1519,7 +1552,12 @@ class Savina usingPlatform: platform andHarness: harness = Value ( accounts at: i put: ((actors createActorFromValue: Account) <-: new: i)] )( public start = ( - 1 to: numTransactions do: [:i | generateWork ] + 1 to: numTransactions do: [:i | + i = (numTransactions / 2) ifTrue: [ + actors snapshot: self. + ]. + generateWork + ] ) public reply: amount = ( @@ -1723,6 +1761,8 @@ class Savina usingPlatform: platform andHarness: harness = Value ( sourceActor <-: next: nextActor. + actors snapshot: self. + ^ completionPP promise ) @@ -2011,6 +2051,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( completionPP:: actors createPromisePair. rootActor:: (actors createActorFromValue: RootActor) <-: new: completionPP resolver. rootActor <-: generateTree. + actors snapshot: self. ^ completionPP promise ) @@ -2049,6 +2090,10 @@ class Savina usingPlatform: platform andHarness: harness = Value ( numTermsReceived:: numTermsReceived + 1. resultArea:: resultArea + result. + numTermsReceived = (numWorkers / 2) ifTrue: [ + actors snapshot: self. + ]. + numTermsReceived = numWorkers ifTrue: [ completionRes resolve: resultArea ] ) @@ -2458,6 +2503,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | completionPP | completionPP:: actors createPromisePair. (actors createActorFromValue: Master) <-: new: completionPP resolver. + actors snapshot: self. ^ completionPP promise ) @@ -2494,7 +2540,8 @@ class Savina usingPlatform: platform andHarness: harness = Value ( 1 to: numWorkers do: [:i | workers at: i put: ((actors createActorFromValue: Worker) <-: new: self)]. - send: (TransferArray new: 0) depth: 0 + send: (TransferArray new: 0) depth: 0. + actors snapshot: self. )( private send: arr depth: depth = ( messageCounter:: messageCounter + 1. From 01d7981e3b8934ffc69eb05259bbeb136675db8a Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Mon, 17 Dec 2018 12:41:41 +0100 Subject: [PATCH 12/80] fix todo --- src/som/interpreter/actors/Actor.java | 4 +++ src/tools/concurrency/TracingActors.java | 4 +-- src/tools/snapshot/SnapshotBackend.java | 34 +++++++++++++++---- src/tools/snapshot/SnapshotRecord.java | 10 +++++- .../DeserializationBuffer.java | 4 +++ .../deserialization/SnapshotParser.java | 9 ++++- 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 3f755e8bf..40e3f4c48 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -400,6 +400,10 @@ public Activity getActivity() { public Actor getCurrentActor() { return currentlyExecutingActor; } + + public void setCurrentActorSnapshot(final Actor current) { + this.currentlyExecutingActor = current; + } } @Override diff --git a/src/tools/concurrency/TracingActors.java b/src/tools/concurrency/TracingActors.java index 739c32b21..1c5f408a2 100644 --- a/src/tools/concurrency/TracingActors.java +++ b/src/tools/concurrency/TracingActors.java @@ -47,7 +47,7 @@ public TracingActor(final VM vm) { super(vm); this.actorId = IdGen.getAndIncrement(); if (VmSettings.SNAPSHOTS_ENABLED) { - snapshotRecord = new SnapshotRecord(); + snapshotRecord = new SnapshotRecord(this); } } @@ -89,7 +89,7 @@ public SnapshotRecord getSnapshotRecord() { * For testing purposes. */ public void replaceSnapshotRecord() { - this.snapshotRecord = new SnapshotRecord(); + this.snapshotRecord = new SnapshotRecord(this); } @Override diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index f415adb3b..4312a3a0f 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -13,6 +13,7 @@ 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; @@ -44,12 +45,13 @@ 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 ConcurrentLinkedQueue> messages; - private static final ArrayList classEnclosures; + 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 ArrayList classEnclosures; + private static final ConcurrentHashMap unfinishedSerializations; // this is a reference to the list maintained by the objectsystem private static EconomicMap loadedModules; @@ -64,6 +66,7 @@ public class SnapshotBackend { buffers = new ConcurrentLinkedQueue<>(); messages = new ConcurrentLinkedQueue<>(); classEnclosures = new ArrayList<>(); + unfinishedSerializations = new ConcurrentHashMap<>(); // identity int, includes mixin info // long outer // essentially this is about capturing the outer @@ -75,6 +78,7 @@ public class SnapshotBackend { buffers = new ConcurrentLinkedQueue<>(); messages = new ConcurrentLinkedQueue<>(); classEnclosures = new ArrayList<>(); + unfinishedSerializations = new ConcurrentHashMap<>(); } else { classDictionary = null; symbolDictionary = null; @@ -82,6 +86,7 @@ public class SnapshotBackend { buffers = null; messages = null; classEnclosures = null; + unfinishedSerializations = null; } } @@ -283,6 +288,14 @@ public static void registerSnapshotBuffer(final SnapshotBuffer sb, messages.add(messageLocations); } + public static void addUnfinishedTodo(final SnapshotRecord sr) { + unfinishedSerializations.put(sr, 0); + } + + public static void removeTodo(final SnapshotRecord sr) { + unfinishedSerializations.remove(sr); + } + public static StructuralProbe getProbe() { assert probe != null; return probe; @@ -310,6 +323,15 @@ public static void writeSnapshot() { // Write Message Locations int offset = writeMessageLocations(fos); offset += writeClassEnclosures(fos); + + // handle the unfinished serialization. + SnapshotBuffer buffer = buffers.peek(); + for (SnapshotRecord sr : unfinishedSerializations.keySet()) { + assert sr.owner != null; + buffer.owner.setCurrentActorSnapshot(sr.owner); + sr.handleTodos(buffer); + } + writeSymbolTable(); // WriteHeapMap diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index fb5aaa812..0ec84b8cd 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -5,6 +5,7 @@ import org.graalvm.collections.EconomicMap; import som.interpreter.Types; +import tools.concurrency.TracingActors.TracingActor; public class SnapshotRecord { @@ -13,6 +14,7 @@ public class SnapshotRecord { * We can get the location of the serialized object in the trace */ private final EconomicMap entries; + protected final TracingActor owner; /** * This list is used to keep track of references to unserialized objects in the actor owning @@ -25,9 +27,10 @@ public class SnapshotRecord { */ private final ConcurrentLinkedQueue externalReferences; - public SnapshotRecord() { + public SnapshotRecord(final TracingActor owner) { this.entries = EconomicMap.create(); this.externalReferences = new ConcurrentLinkedQueue<>(); + this.owner = owner; } /** @@ -58,6 +61,7 @@ public void addObjectEntry(final Object o, final long offset) { } public void handleTodos(final SnapshotBuffer sb) { + SnapshotBackend.removeTodo(this); while (!externalReferences.isEmpty()) { FarRefTodo frt = externalReferences.poll(); @@ -70,6 +74,7 @@ public void handleTodos(final SnapshotBuffer sb) { frt.resolve(getObjectPointer(frt.target)); } } + } /** @@ -91,6 +96,9 @@ public void farReference(final Object o, final SnapshotBuffer other, if (l != null) { other.putLongAt(destination, l); } else { + if (externalReferences.isEmpty()) { + SnapshotBackend.addUnfinishedTodo(this); + } externalReferences.offer(new FarRefTodo(other, destination, o)); } } diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index 0525d8f86..3ebd4b082 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -63,6 +63,10 @@ public boolean allreadyDeserialized(final long reference) { return deserialized.containsKey(reference); } + public int getNumObjects() { + return deserialized.size(); + } + private void printPosition(final long current) { // System.out.print(depth + " - " + getAbsolute(current) + " in " + (current >> 48) + " "); } diff --git a/src/tools/snapshot/deserialization/SnapshotParser.java b/src/tools/snapshot/deserialization/SnapshotParser.java index 4207b6729..0b469b9f3 100644 --- a/src/tools/snapshot/deserialization/SnapshotParser.java +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -37,6 +37,7 @@ public final class SnapshotParser { private VM vm; private EconomicMap outerMap; private DeserializationBuffer db; + private int objectcnt; private SnapshotParser(final VM vm) { this.vm = vm; @@ -166,6 +167,7 @@ private void parseMetaData() { throw new RuntimeException(e); } finally { // prevent usage after closing + objectcnt = db.getNumObjects(); db = null; } } @@ -190,7 +192,8 @@ private static void parseSymbols() { public static long getFileOffset(final long address) { long threadId = address >> SnapshotBuffer.THREAD_SHIFT; - assert parser.heapOffsets.containsKey(threadId); + assert parser.heapOffsets.containsKey( + threadId) : "Probably some actor didn't get to finish it's todo list"; return parser.heapOffsets.get(threadId); } @@ -243,4 +246,8 @@ public static SPromise getResultPromise() { public static DeserializationBuffer getDeserializationBuffer() { return parser.db; } + + public static int getObjectCnt() { + return parser.objectcnt; + } } From a11d8ee7fec786083fb0a80f973c36bea99f869d Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Mon, 17 Dec 2018 14:17:46 +0100 Subject: [PATCH 13/80] add test script --- tests/snapshot-replay/test.sh | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 tests/snapshot-replay/test.sh diff --git a/tests/snapshot-replay/test.sh b/tests/snapshot-replay/test.sh new file mode 100755 index 000000000..9832b0ce8 --- /dev/null +++ b/tests/snapshot-replay/test.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# quit on first error +set -e + +if [ "$1" = "1" ] +then + declare -a Savina=( + "PingPong 1 0 40000" + "Counting 1 0 50000" + "ForkJoinThroughput 1 0 300:60" + "ForkJoinActorCreation 1 0 4000" + "ThreadRing 1 0 100:10000" + "Chameneos 1 0 100:10000" + "BigContention 1 0 20:12" + #"ConcurrentDictionary 1 0 5:100:5" chicken egg problem with entry class + #"ConcurrentSortedLinkedList 1 0 10:1500:10:1" + "ProducerConsumerBoundedBuffer 1 0 40:10:10:60" + "Philosophers 1 0 20:5000" + + ) +else + declare -a Savina=( + #"SleepingBarber 1 0 2500:1000:1000:1000" + "CigaretteSmokers 1 0 10000:200" + "LogisticsMapSeries 1 0 25000:10:346" + #"BankTransaction 1 0 1000:100000" chicken egg + #"RadixSort 1 0 50000:65536:74755" #not reliable + #"UnbalancedCobwebbedTree 1 0 100000:10:500:100" #some kind of memory leak or infinite deserialization... + "TrapezoidalApproximation 1 0 100:1000000:1:5" + #"AStarSearch 1 0 100:20" #same issue as unblanced + "NQueens 1 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 + echo "$args" + echo "Tracing:" + $SOM_DIR/som -G -as -at -JXmx1500m -JXss4096k core-lib/Benchmarks/AsyncHarness.ns SavinaSnap.$args + echo "" + echo "Replay:" + $SOM_DIR/som -G -as -r -JXmx1500m -JXss4096k -vmd core-lib/Benchmarks/AsyncHarness.ns SavinaSnap.$args + echo "" + echo "========================================================" + echo "" +done From e8182cc2d55645fc3a6db88f04fd6a80188bbbdb Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:29:25 +0100 Subject: [PATCH 14/80] Message Location Fix --- src/som/interpreter/actors/Actor.java | 6 +- src/tools/snapshot/SnapshotRecord.java | 8 ++ .../deserialization/SnapshotParser.java | 87 +++++++++---------- 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 40e3f4c48..2ba4ca438 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -289,7 +289,8 @@ protected void processCurrentMessages(final ActorProcessingThread currentThread, sb.getRecord().handleTodos(sb); long loc = firstMessage.serialize(sb); if (loc != -1) { - sb.getOwner().addMessageLocation(((TracingActor) actor).getActorId(), + sb.getOwner().addMessageLocation( + ((TracingActor) actor).getSnapshotRecord().getMessageIdentifier(), sb.calculateReference(loc)); } } @@ -300,7 +301,8 @@ protected void processCurrentMessages(final ActorProcessingThread currentThread, if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { long loc = msg.serialize(currentThread.getSnapshotBuffer()); if (loc != -1) { - currentThread.addMessageLocation(((TracingActor) actor).getActorId(), + currentThread.addMessageLocation( + ((TracingActor) actor).getSnapshotRecord().getMessageIdentifier(), currentThread.getSnapshotBuffer().calculateReference(loc)); } } diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 0ec84b8cd..0932712bc 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -15,6 +15,7 @@ public class SnapshotRecord { */ private final EconomicMap entries; protected final TracingActor owner; + private int msgCnt; /** * This list is used to keep track of references to unserialized objects in the actor owning @@ -31,6 +32,13 @@ public SnapshotRecord(final TracingActor owner) { this.entries = EconomicMap.create(); this.externalReferences = new ConcurrentLinkedQueue<>(); this.owner = owner; + msgCnt = 0; + } + + public long getMessageIdentifier() { + long result = (((long) owner.getActorId()) << 32) | msgCnt; + msgCnt++; + return result; } /** diff --git a/src/tools/snapshot/deserialization/SnapshotParser.java b/src/tools/snapshot/deserialization/SnapshotParser.java index 0b469b9f3..ab1bf741e 100644 --- a/src/tools/snapshot/deserialization/SnapshotParser.java +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -9,7 +9,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; -import java.util.ArrayList; +import java.util.PriorityQueue; import org.graalvm.collections.EconomicMap; @@ -30,14 +30,14 @@ public final class SnapshotParser { private static SnapshotParser parser; - private final EconomicMap heapOffsets; - private final EconomicMap> messageLocations; - private SPromise resultPromise; - private ReplayActor currentActor; - private VM vm; - private EconomicMap outerMap; - private DeserializationBuffer db; - private int objectcnt; + private EconomicMap heapOffsets; + private EconomicMap> messageLocations; + private SPromise resultPromise; + private ReplayActor currentActor; + private VM vm; + private EconomicMap outerMap; + private DeserializationBuffer db; + private int objectcnt; private SnapshotParser(final VM vm) { this.vm = vm; @@ -74,13 +74,15 @@ private void parseMetaData() { long numMessages = b.getLong() / 2; for (int i = 0; i < numMessages; i++) { ensureRemaining(Long.BYTES * 2, b, channel); - int actorId = (int) b.getLong(); + 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 ArrayList<>()); + messageLocations.put(actorId, new PriorityQueue<>()); } - messageLocations.get(actorId).add(location); + messageLocations.get(actorId).add(new MessageLocation(msgNo, location)); } long numOuters = b.getLong(); @@ -91,32 +93,6 @@ private void parseMetaData() { outerMap.put(identity, outer); } - // ensureRemaining(Short.BYTES, b, channel); - // short numModules = b.getShort(); - // for (int i = 0; i < numModules; i++) { - // ensureRemaining(Short.BYTES, b, channel); - // short symId = b.getShort(); - // SSymbol sym = SnapshotBackend.getSymbolForId(symId); - // URI uri = new URI(sym.getString()); - // String[] components = sym.getString().split(":"); - // assert components.length == 2; - // String path = components[0]; - // - // // I think extension modules are not added to the list we use... - // // for now this is not an issue but later we may need to solve this for acme - // // My understanding is that there is exactly one SClass object, which is returned when - // // loading the module. - // // By loading the module multiple times you would get a new SClass every time. - // if (path.endsWith(SystemPrims.EXTENSION_EXT)) { - // vm.loadExtensionModule(path); - // } else { - // MixinDefinition module; - // module = vm.loadModule(path); - // // TODO looks like the module paths we try to load are problematic. - // // probably a hashtag too many. - // } - // } - ensureRemaining(Long.BYTES * 2, b, channel); long resultPromiseLocation = b.getLong(); long numHeaps = b.getLong(); @@ -130,12 +106,9 @@ private void parseMetaData() { // At this point we now have read all of the metadata and can begin the process of // inflating the snapshot. - // EconomicMap actors = EconomicMap.create(); - - // lets create the Actor objects first, and add them to our list + // make sure all the actors exist, we resuse the mapping in ReplayActor for (int id : messageLocations.getKeys()) { SnapshotBackend.lookupActor(id); - // actors.put(id, ra); } db = new FileDeserializationBuffer(channel); @@ -143,14 +116,15 @@ private void parseMetaData() { // 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()) { - ArrayList locations = messageLocations.get(id); - for (long location : locations) { + PriorityQueue locations = messageLocations.get(id); + MessageLocation ml = locations.poll(); + while (ml != null) { // Deserialilze message currentActor = ReplayActor.getActorWithId(id); - // System.out.println( - // "Message at: " + (((int) location) + getFileOffset(location))); - EventualMessage em = (EventualMessage) db.deserializeWithoutContext(location); + EventualMessage em = (EventualMessage) db.deserializeWithoutContext(ml.location); + db.doUnserialized(); currentActor.sendSnapshotMessage(em); + ml = locations.poll(); } } @@ -159,6 +133,9 @@ private void parseMetaData() { resultPromise = (SPromise) db.deserialize(resultPromiseLocation); } + outerMap = null; + messageLocations = null; + assert resultPromise != null : "The result promise was not found"; } catch (FileNotFoundException e) { e.printStackTrace(); @@ -204,7 +181,6 @@ public static SObjectWithClass getOuterForClass(final int identity) { long reference = parser.outerMap.get(identity); Object o = parser.db.getReference(reference); long pos = ((int) reference) + SnapshotParser.getFileOffset(reference); - // System.out.print("Outer - " + pos + " in " + (reference >> 48) + " "); if (!parser.db.allreadyDeserialized(reference)) { result = (SObjectWithClass) parser.db.deserialize(reference); } else if (parser.db.needsFixup(o)) { @@ -250,4 +226,19 @@ public static DeserializationBuffer getDeserializationBuffer() { public static int getObjectCnt() { return parser.objectcnt; } + + private class MessageLocation implements Comparable { + final int msgNo; + final long location; + + public 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); + } + } } From ce7f7ce5437d4eaabc84a121354f9e3d6d76a9d7 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:40:13 +0100 Subject: [PATCH 15/80] keep track of promise messages --- src/tools/snapshot/SnapshotBuffer.java | 4 ++- src/tools/snapshot/SnapshotRecord.java | 27 ++++++++++++++++--- .../nodes/MessageSerializationNode.java | 16 +++++------ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/tools/snapshot/SnapshotBuffer.java b/src/tools/snapshot/SnapshotBuffer.java index c00b79f8f..b6faca40b 100644 --- a/src/tools/snapshot/SnapshotBuffer.java +++ b/src/tools/snapshot/SnapshotBuffer.java @@ -1,6 +1,7 @@ package tools.snapshot; import som.interpreter.actors.Actor.ActorProcessingThread; +import som.interpreter.actors.EventualMessage; import som.vm.VmSettings; import som.vm.constants.Classes; import som.vmobjects.SClass; @@ -62,11 +63,12 @@ public int addObjectWithFields(final Object o, final SClass clazz, 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; TracingActor ta = (TracingActor) owner.getCurrentActor(); + getRecord().addObjectEntry(msg, calculateReference(oldPos)); // owner.addMessageLocation(ta.getActorId(), calculateReference(oldPos)); this.putIntAt(this.position, Classes.messageClass.getIdentity()); diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 0932712bc..1764986d0 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -5,6 +5,7 @@ import org.graalvm.collections.EconomicMap; import som.interpreter.Types; +import som.interpreter.actors.EventualMessage.PromiseMessage; import tools.concurrency.TracingActors.TracingActor; @@ -76,9 +77,12 @@ public void handleTodos(final SnapshotBuffer sb) { // ignore todos from a different snapshot if (frt.referer.snapshotVersion == sb.snapshotVersion) { if (!this.containsObjectUnsync(frt.target)) { - Types.getClassOf(frt.target).serialize(frt.target, sb); + if (frt.target instanceof PromiseMessage) { + ((PromiseMessage) frt.target).forceSerialize(sb); + } else { + Types.getClassOf(frt.target).serialize(frt.target, sb); + } } - frt.resolve(getObjectPointer(frt.target)); } } @@ -111,7 +115,24 @@ public void farReference(final Object o, final SnapshotBuffer other, } } - private final class FarRefTodo { + public void farReferenceMessage(final PromiseMessage pm, final SnapshotBuffer other, + final int destination) { + Long l; + synchronized (entries) { + l = entries.get(pm); + } + + if (l != null) { + other.putLongAt(destination, l); + } else { + if (externalReferences.isEmpty()) { + SnapshotBackend.addUnfinishedTodo(this); + } + externalReferences.offer(new FarRefTodo(other, destination, pm)); + } + } + + public static final class FarRefTodo { private final SnapshotBuffer referer; private final int referenceOffset; final Object target; diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 2ac7d40c1..d0190bb0c 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -115,7 +115,7 @@ protected long doDirectMessage(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 base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.DirectMessage, dm.getSelector(), (TracingActor) dm.getSender(), @@ -135,7 +135,7 @@ protected long doDirectMessageNoResolver(final DirectMessage dm, final SnapshotB Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.DirectMessageNR, dm.getSelector(), @@ -155,7 +155,7 @@ protected long doCallbackMessage(final PromiseCallbackMessage dm, final Snapshot Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.CallbackMessage, dm.getSelector(), @@ -179,7 +179,7 @@ protected long doCallbackMessageNoResolver(final PromiseCallbackMessage dm, Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.CallbackMessageNR, dm.getSelector(), @@ -204,7 +204,7 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.PromiseMessage, dm.getSelector(), @@ -233,7 +233,7 @@ protected long doPromiseMessageNoResolver(final PromiseSendMessage dm, int payload = COMMONALITY_BYTES + Long.BYTES + Integer.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.PromiseMessageNR, dm.getSelector(), @@ -258,7 +258,7 @@ protected long doUndeliveredPromiseMessage(final PromiseSendMessage dm, Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; serializeResolver(resolver, sb); @@ -279,7 +279,7 @@ protected long doUndeliveredPromiseMessageNoResolver(final PromiseSendMessage dm Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + 1 + (args.length * Long.BYTES); - int base = sb.addMessage(payload); + int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; doCommonalities(MessageType.UndeliveredPromiseMessageNR, dm.getSelector(), From daeda0b507a30557ef11ee31bac996300a6e15eb Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:40:40 +0100 Subject: [PATCH 16/80] message location part 2 --- src/tools/concurrency/TracingActivityThread.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/concurrency/TracingActivityThread.java b/src/tools/concurrency/TracingActivityThread.java index d9e11b9d5..dc1d79fae 100644 --- a/src/tools/concurrency/TracingActivityThread.java +++ b/src/tools/concurrency/TracingActivityThread.java @@ -169,8 +169,8 @@ public final void addExternalData(final Object data) { } } - public final void addMessageLocation(final int actorId, final long messageAdress) { - messageLocations.add((long) actorId); + public final void addMessageLocation(final long actorId, final long messageAdress) { + messageLocations.add(actorId); messageLocations.add(messageAdress); } From 6c4ffc5139c35b99f093af3784624dd6e3ea4a14 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:45:27 +0100 Subject: [PATCH 17/80] promise changes --- .../interpreter/actors/EventualMessage.java | 17 +++++++- src/som/interpreter/actors/SPromise.java | 24 +++++++++++ .../nodes/PromiseSerializationNodes.java | 40 +++++++++++++------ 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index b7c15ad1c..3ee32f7b9 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -84,6 +84,9 @@ public long serialize(final SnapshotBuffer sb) { if (sb.needsToBeSnapshot(getMessageId())) { // Not sure if this is optimized, worst case need to duplicate this for all messages + if (sb.getRecord().containsObject(this)) { + return sb.getRecord().getObjectPointer(this); + } return rm.getSerializer().execute(this, sb); } else { // need to be careful, might interfere with promise serialization... @@ -92,6 +95,9 @@ public long serialize(final SnapshotBuffer sb) { } 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); } @@ -263,6 +269,8 @@ public final Actor getSender() { */ public abstract void setPromise(SPromise promise); + public abstract boolean isDelivered(); + @Override public boolean getHaltOnPromiseMessageResolution() { return getPromise().getHaltOnResolution(); @@ -310,8 +318,7 @@ 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 && !VmSettings.REPLAY) { - this.messageId = Math.min(this.messageId, - ActorProcessingThread.currentThread().getSnapshotId()); + this.messageId = ActorProcessingThread.currentThread().getSnapshotId(); } } @@ -326,6 +333,7 @@ public Actor getTarget() { return target; } + @Override public boolean isDelivered() { return target != null; } @@ -436,6 +444,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/SPromise.java b/src/som/interpreter/actors/SPromise.java index a22f1348a..ca4fbd3a8 100644 --- a/src/som/interpreter/actors/SPromise.java +++ b/src/som/interpreter/actors/SPromise.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.source.SourceSection; +import som.interpreter.SomLanguage; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.vm.VmSettings; import som.vmobjects.SClass; @@ -129,6 +130,29 @@ 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 = value; + this.resolutionState = resolutionState; + ForkJoinPool pool; + pool = SomLanguage.getCurrent().getVM().getActorPool(); + + if (schedule) { + if (resolutionState == Resolution.SUCCESSFUL) { + SResolver.scheduleAllWhenResolvedUnsync(this, value, resolver, pool, haltOnResolution, + null); + } else { + assert resolutionState == Resolution.ERRONEOUS; + SResolver.scheduleAllOnErrorUnsync(this, value, resolver, pool, haltOnResolution); + } + // resolveChainedPromisesUnsync(resolutionState, this, resolver, current, actorPool, + // haltOnResolution, + // whenResolvedProfile); + } + + } + public long getPromiseId() { return 0; } diff --git a/src/tools/snapshot/nodes/PromiseSerializationNodes.java b/src/tools/snapshot/nodes/PromiseSerializationNodes.java index 8d4bfa2a9..0e640917e 100644 --- a/src/tools/snapshot/nodes/PromiseSerializationNodes.java +++ b/src/tools/snapshot/nodes/PromiseSerializationNodes.java @@ -95,8 +95,8 @@ public void doResolved(final SPromise prom, final SnapshotBuffer sb) { sb.putIntAt(base + 1 + +Integer.BYTES + Long.BYTES, ((STracingPromise) prom).getResolvingActor()); base += (1 + Integer.BYTES + Integer.BYTES + Long.BYTES); - base = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); - base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); + base = serializeDeliveredMessages(base, nwr, whenRes, whenResExt, sb); + base = serializeDeliveredMessages(base, noe, onError, onErrorExt, sb); serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); } @@ -131,8 +131,8 @@ public void doUnresolved(final SPromise prom, final SnapshotBuffer sb) { sb.putByteAt(base, (byte) 0); sb.putIntAt(base + 1, ((TracingActor) prom.getOwner()).getActorId()); base += 1 + Integer.BYTES; - base = serializeWhenResolvedMsgs(base, nwr, whenRes, whenResExt, sb); - base = serializeOnErrorMsgs(base, noe, onError, onErrorExt, sb); + base = serializeMessages(base, nwr, whenRes, whenResExt, sb); + base = serializeMessages(base, noe, onError, onErrorExt, sb); serializeChainedPromises(base, ncp, chainedProm, chainedPromExt, sb); } @@ -146,7 +146,7 @@ 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; @@ -166,26 +166,37 @@ 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 int serializeDeliveredMessages(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, onError.forceSerialize(sb)); + doMessage(whenRes, base, sb); base += Long.BYTES; if (cnt > 1) { - for (int i = 0; i < onErrorExt.size(); i++) { - sb.putLongAt(base + i * Long.BYTES, onErrorExt.get(i).forceSerialize(sb)); + for (int i = 0; i < whenResExt.size(); i++) { + doMessage(whenResExt.get(i), base + i * Long.BYTES, sb); } - base += onErrorExt.size() * Long.BYTES; + base += whenResExt.size() * Long.BYTES; } } return base; } + private void doMessage(final PromiseMessage pm, final int location, + final SnapshotBuffer sb) { + if (pm.isDelivered() + && pm.getTarget() != pm.getPromise().getOwner()) { + TracingActor ta = (TracingActor) pm.getTarget(); + ta.getSnapshotRecord().farReferenceMessage(pm, sb, location); + } else { + sb.putLongAt(location, pm.forceSerialize(sb)); + } + } + private void serializeChainedPromises(final int start, final int cnt, final SPromise chainedProm, final ArrayList chainedPromExt, final SnapshotBuffer sb) { @@ -247,9 +258,14 @@ private SPromise deserializeCompletedPromise(final byte state, int chainedPromCnt = sb.getShort(); for (int i = 0; i < chainedPromCnt; i++) { SPromise remote = (SPromise) sb.getReference(); + boolean complete = remote.isCompleted(); + p.addChainedPromise(remote); - } + remote.resolveFromSnapshot(value, p.getResolutionStateUnsync(), + SnapshotBackend.lookupActor(resolver), !complete); + ((STracingPromise) remote).setResolvingActorForSnapshot(resolver); + } return p; } From ef9febc4c9e9d44ef0b5435504debe9a820e3f98 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:46:53 +0100 Subject: [PATCH 18/80] fix execute race --- src/som/interpreter/actors/Actor.java | 11 +++++++++++ src/tools/concurrency/TracingActors.java | 6 +----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 2ba4ca438..2c87dda4c 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -184,6 +184,17 @@ public synchronized void sendSnapshotMessage(final EventualMessage 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; diff --git a/src/tools/concurrency/TracingActors.java b/src/tools/concurrency/TracingActors.java index 1c5f408a2..c1d151b92 100644 --- a/src/tools/concurrency/TracingActors.java +++ b/src/tools/concurrency/TracingActors.java @@ -230,11 +230,7 @@ public synchronized void send(final EventualMessage msg, final ForkJoinPool acto public static void scheduleAllActors(final ForkJoinPool actorPool) { for (ReplayActor ra : actorList.values()) { - - if (ra.firstMessage != null && !ra.isExecuting) { - ra.isExecuting = true; - ra.execute(actorPool); - } + ra.executeIfNecessarry(actorPool); } } From 22812a2acd3fb47101bd5987a43bf082cd39d3f2 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:53:35 +0100 Subject: [PATCH 19/80] fix deserialization circles --- .../DeserializationBuffer.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/tools/snapshot/deserialization/DeserializationBuffer.java b/src/tools/snapshot/deserialization/DeserializationBuffer.java index 3ebd4b082..aee3f9b3d 100644 --- a/src/tools/snapshot/deserialization/DeserializationBuffer.java +++ b/src/tools/snapshot/deserialization/DeserializationBuffer.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; +import java.util.ArrayList; import org.graalvm.collections.EconomicMap; @@ -23,16 +24,19 @@ public class DeserializationBuffer { private final EconomicMap deserialized; private long lastRef; int depth = 0; + private ArrayList unserialized; public DeserializationBuffer(final byte[] backing) { 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() { @@ -112,6 +116,11 @@ public Object deserializeWithoutContext(final long current) { depth++; SClass clazz = SnapshotBackend.lookupClass(cId); + if (clazz == null) { + unserialized.add(current); + depth--; + return null; + } Object o = clazz.getSerializer().deserialize(this); @@ -140,6 +149,13 @@ public Object getReference() { depth++; SClass clazz = SnapshotBackend.lookupClass(cId); + if (clazz == null) { + unserialized.add(reference); + depth--; + position(current); + return null; + } + Object o = clazz.getSerializer().deserialize(this); depth--; // continue with current object @@ -208,6 +224,17 @@ public synchronized void fixUpIfNecessary(final long reference, final Object res } } + 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) { fixUpIfNecessary(lastRef, o); deserialized.put(lastRef, o); From 2c535074c3517b8de15de2286e67d45a3133f731 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:53:56 +0100 Subject: [PATCH 20/80] fix empty array deserialization --- src/tools/snapshot/SnapshotBackend.java | 17 ++++++++++++----- .../nodes/AbstractArraySerializationNode.java | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index 4312a3a0f..da62cde48 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -115,7 +115,10 @@ public static void registerLoadedModules(final EconomicMap public static SClass lookupClass(final int id) { assert VmSettings.TRACK_SNAPSHOT_ENTITIES; if (classDictionary.containsKey(id)) { - // this method isn't designed for fixup + if (!(classDictionary.get(id) instanceof SClass)) { + return null; + } + return (SClass) classDictionary.get(id); } @@ -326,10 +329,14 @@ public static void writeSnapshot() { // handle the unfinished serialization. SnapshotBuffer buffer = buffers.peek(); - for (SnapshotRecord sr : unfinishedSerializations.keySet()) { - assert sr.owner != null; - buffer.owner.setCurrentActorSnapshot(sr.owner); - sr.handleTodos(buffer); + + while (!unfinishedSerializations.isEmpty()) { + for (SnapshotRecord sr : unfinishedSerializations.keySet()) { + assert sr.owner != null; + unfinishedSerializations.remove(sr); + buffer.owner.setCurrentActorSnapshot(sr.owner); + sr.handleTodos(buffer); + } } writeSymbolTable(); diff --git a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java index 326a26c17..1e0f50c39 100644 --- a/src/tools/snapshot/nodes/AbstractArraySerializationNode.java +++ b/src/tools/snapshot/nodes/AbstractArraySerializationNode.java @@ -155,7 +155,7 @@ protected Object parseBackingStorage(final DeserializationBuffer sb) { backing = oa; break; case TYPE_EMPTY: - backing = sb.getInt(); + backing = len; break; default: throw new IllegalArgumentException(); From 831601d667c40843273094078731592b352fa763 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:56:07 +0100 Subject: [PATCH 21/80] change class enclosure handling --- src/tools/snapshot/SnapshotBackend.java | 54 +++++++++++-------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index da62cde48..0334fe776 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -17,12 +17,14 @@ import java.util.concurrent.ConcurrentLinkedQueue; import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.MapCursor; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import som.VM; import som.compiler.MixinDefinition; import som.interpreter.actors.Actor; +import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.EventualMessage; import som.interpreter.actors.SPromise; import som.interpreter.nodes.InstantiationNode.ClassInstantiationNode; @@ -33,6 +35,7 @@ import som.vmobjects.SInvokable; import som.vmobjects.SObjectWithClass; import som.vmobjects.SSymbol; +import tools.concurrency.TracingActivityThread; import tools.concurrency.TracingActors.ReplayActor; import tools.concurrency.TracingActors.TracingActor; import tools.concurrency.TracingBackend; @@ -50,7 +53,7 @@ public class SnapshotBackend { private static final StructuralProbe probe; private static final ConcurrentLinkedQueue buffers; private static final ConcurrentLinkedQueue> messages; - private static final ArrayList classEnclosures; + private static final EconomicMap classEnclosures; private static final ConcurrentHashMap unfinishedSerializations; // this is a reference to the list maintained by the objectsystem @@ -65,7 +68,7 @@ public class SnapshotBackend { probe = new StructuralProbe(); buffers = new ConcurrentLinkedQueue<>(); messages = new ConcurrentLinkedQueue<>(); - classEnclosures = new ArrayList<>(); + classEnclosures = EconomicMap.create(); unfinishedSerializations = new ConcurrentHashMap<>(); // identity int, includes mixin info // long outer @@ -77,7 +80,7 @@ public class SnapshotBackend { probe = null; buffers = new ConcurrentLinkedQueue<>(); messages = new ConcurrentLinkedQueue<>(); - classEnclosures = new ArrayList<>(); + classEnclosures = EconomicMap.create(); unfinishedSerializations = new ConcurrentHashMap<>(); } else { classDictionary = null; @@ -103,7 +106,7 @@ public static void registerSymbol(final SSymbol sym) { symbolDictionary.put(sym.getSymbolId(), sym); } - public static void registerClass(final SClass clazz) { + public static synchronized void registerClass(final SClass clazz) { assert VmSettings.TRACK_SNAPSHOT_ENTITIES; classDictionary.put(clazz.getIdentity(), clazz); } @@ -154,6 +157,7 @@ private static SClass createSClass(final int id) { // Step 1: install placeholder classDictionary.put(id, null); + // System.out.println("creating Class" + mixin.getIdentifier() + " : " + (short) id); // Step 2: get outer object SObjectWithClass enclosingObject = SnapshotParser.getOuterForClass(id); @@ -164,8 +168,6 @@ private static SClass createSClass(final int id) { mixin.getSuperclassAndMixinResolutionInvokable().createCallTarget() .call(new Object[] {enclosingObject}); - // System.out.println("creating Class" + mixin.getIdentifier() + " : " + (short) id); - ClassFactory factory = mixin.createClassFactory(superclassAndMixins, false, false, false, UninitializedObjectSerializationNodeFactory.getInstance()); @@ -350,42 +352,32 @@ public static void writeSnapshot() { fos.getChannel().write(ByteBuffer.wrap(sb.getRawBuffer(), 0, sb.position())); fos.flush(); } + } catch (IOException e1) { throw new RuntimeException(e1); } } - private static int writeLoadedModules(final FileOutputStream fos) throws IOException { - int size = (loadedModules.size() + 1) * Short.BYTES; - ByteBuffer bb = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - // by adding the uri strings to the symbol table we can use a compact represenation as part - // of the snap file. - - bb.putShort((short) loadedModules.size()); - for (MixinDefinition module : loadedModules.getValues()) { - bb.putShort(module.getIdentifier().getSymbolId()); - } - - bb.rewind(); - fos.getChannel().write(bb); - fos.flush(); - return size; - } - private static int writeClassEnclosures(final FileOutputStream fos) throws IOException { int size = (classEnclosures.size() * 2 + 1) * Long.BYTES; ByteBuffer bb = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(classEnclosures.size()); - for (SClass clazz : classEnclosures) { + MapCursor cursor = classEnclosures.getEntries(); + + // just use the first buffer available; that object isn't used anywhere else + SnapshotBuffer buffer = buffers.peek(); + + while (cursor.advance()) { + SClass clazz = cursor.getKey(); + TracingActor owner = cursor.getValue(); + bb.putLong(clazz.getIdentity()); SObjectWithClass outer = clazz.getEnclosingObject(); - // TODO if necessary keep actual ownership information, for now we assume all the class - // creation is happening in the main actor. - TracingActor owner = (TracingActor) vm.getMainActor(); + if (!owner.getSnapshotRecord().containsObjectUnsync(outer)) { - // just use the first buffer available; that object isn't used anywhere else - outer.getSOMClass().serialize(outer, buffers.peek()); + buffer.owner.setCurrentActorSnapshot(owner); + outer.getSOMClass().serialize(outer, buffer); } bb.putLong(owner.getSnapshotRecord().getObjectPointer(outer)); } @@ -481,8 +473,10 @@ public static void registerResultPromise(final SPromise promise) { } public static void registerClassEnclosure(final SClass clazz) { + ActorProcessingThread current = + (ActorProcessingThread) TracingActivityThread.currentThread(); synchronized (classEnclosures) { - classEnclosures.add(clazz); + classEnclosures.put(clazz, (TracingActor) current.getCurrentActor()); } } } From 9926f6b3734d8145676f8a437fdc27e190da09c6 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 10:56:57 +0100 Subject: [PATCH 22/80] SavinaSnap changes --- build.xml | 11 ++++++++++- core-lib/Benchmarks/SavinaSnap.ns | 24 ++++++++++++++++-------- tests/snapshot-replay/test.sh | 14 +++++++------- 3 files changed, 33 insertions(+), 16 deletions(-) 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 index e66984154..f5a5e8c4d 100644 --- a/core-lib/Benchmarks/SavinaSnap.ns +++ b/core-lib/Benchmarks/SavinaSnap.ns @@ -639,9 +639,13 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | 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 ]. + 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 = ( @@ -669,7 +673,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( ) public read: sender key: key = ( - sender <-: result: (dataMap at: key) + sender <-: result: (dataMap at: key). ) public endWork = ( @@ -1588,6 +1592,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( private lastSender ::= nil. private waitingForReply ::= false. private requests = Vector new. + private id ::= id. | )( class Request credit: amount to: destAccount reply: sender = ( @@ -1642,6 +1647,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | r | r:: result round. + numTransactions = 100 ifTrue: [ ^ r = 52558 ]. numTransactions = 1000 ifTrue: [ ^ r = 516215 ]. numTransactions = 10000 ifTrue: [ ^ r = 4975454 ]. numTransactions = 50000 ifTrue: [ ^ r = 25057792 ]. @@ -1927,7 +1933,8 @@ class Savina usingPlatform: platform andHarness: harness = Value ( size:: size + numChildren. - children do: [:c | c <-: tryGeneratedChildren ] + children do: [:c | c <-: tryGeneratedChildren ]. + actors snapshot: self. ) public shouldGenerateChildren: child height: childHeight = ( @@ -1992,14 +1999,15 @@ class Savina usingPlatform: platform andHarness: harness = Value ( 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 | + | myArrayId childrenHeight idValue cnt | myArrayId:: myId % numChildren. myParent <-: updateGrant: myArrayId. childrenHeight:: myHeight + 1. @@ -2012,6 +2020,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( ]. hasChildren:: true. + cnt:: 0. children do: [:c | c <-: tryGeneratedChildren ]. ) @@ -2051,7 +2060,6 @@ class Savina usingPlatform: platform andHarness: harness = Value ( completionPP:: actors createPromisePair. rootActor:: (actors createActorFromValue: RootActor) <-: new: completionPP resolver. rootActor <-: generateTree. - actors snapshot: self. ^ completionPP promise ) diff --git a/tests/snapshot-replay/test.sh b/tests/snapshot-replay/test.sh index 9832b0ce8..5fb4d82b8 100755 --- a/tests/snapshot-replay/test.sh +++ b/tests/snapshot-replay/test.sh @@ -12,9 +12,9 @@ then "ThreadRing 1 0 100:10000" "Chameneos 1 0 100:10000" "BigContention 1 0 20:12" - #"ConcurrentDictionary 1 0 5:100:5" chicken egg problem with entry class - #"ConcurrentSortedLinkedList 1 0 10:1500:10:1" - "ProducerConsumerBoundedBuffer 1 0 40:10:10:60" + "ConcurrentDictionary 1 0 5:100:5" + "ConcurrentSortedLinkedList 1 0 10:1500:10:1" + #"ProducerConsumerBoundedBuffer 1 0 40:10:10:60" "Philosophers 1 0 20:5000" ) @@ -23,11 +23,11 @@ else #"SleepingBarber 1 0 2500:1000:1000:1000" "CigaretteSmokers 1 0 10000:200" "LogisticsMapSeries 1 0 25000:10:346" - #"BankTransaction 1 0 1000:100000" chicken egg - #"RadixSort 1 0 50000:65536:74755" #not reliable - #"UnbalancedCobwebbedTree 1 0 100000:10:500:100" #some kind of memory leak or infinite deserialization... + "BankTransaction 1 0 500:10000" + #"RadixSort 1 0 50000:65536:74755" + "UnbalancedCobwebbedTree 1 0 100000:10:500:100" "TrapezoidalApproximation 1 0 100:1000000:1:5" - #"AStarSearch 1 0 100:20" #same issue as unblanced + #"AStarSearch 1 0 100:20" "NQueens 1 0 20:10:4" ) fi From 966e3dae8bd70aa4ef68e455ae36a1773141a5f0 Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 11:00:35 +0100 Subject: [PATCH 23/80] improve snapshot replay output --- src/som/vm/ObjectSystem.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index ccdf4d504..f8d894945 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -594,16 +594,17 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m @TruffleBoundary public int executeApplicationFromSnapshot(final SObjectWithoutFields vmMirror) { mainThreadCompleted = new CompletableFuture<>(); - System.out.println("Attemting to execute from Snapshot"); + Output.println("Parsing Snapshot..."); ObjectTransitionSafepoint.INSTANCE.register(); Object platform = platformModule.instantiateObject(platformClass, vmMirror); ObjectTransitionSafepoint.INSTANCE.unregister(); SnapshotBackend.initialize(vm); SnapshotParser.inflate(vm); - // System.out.println("Done with inflation!!!"); + + Output.println( + "Finished restoring snapshot with " + SnapshotParser.getObjectCnt() + " Objects"); ReplayActor.scheduleAllActors(vm.getActorPool()); - // System.out.println("Done scheduling actors"); SPromise result = SnapshotParser.getResultPromise(); return handlePromiseResult(result); From fb3d14e112ee4363d770d42816fad497c5feec1a Mon Sep 17 00:00:00 2001 From: Dominik Aumayr Date: Thu, 20 Dec 2018 11:01:36 +0100 Subject: [PATCH 24/80] avoid race --- src/tools/snapshot/SnapshotRecord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 1764986d0..6f1d456cd 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -70,7 +70,7 @@ public void addObjectEntry(final Object o, final long offset) { } public void handleTodos(final SnapshotBuffer sb) { - SnapshotBackend.removeTodo(this); + // SnapshotBackend.removeTodo(this); while (!externalReferences.isEmpty()) { FarRefTodo frt = externalReferences.poll(); From aa2bf3c6cf666c02549078e3bdfaec94a6959996 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Mon, 17 Dec 2018 18:56:44 +0100 Subject: [PATCH 25/80] Move snapshotting into ReceivedRootNode Signed-off-by: Stefan Marr --- src/som/interpreter/actors/Actor.java | 19 ------------------- .../interpreter/actors/ReceivedRootNode.java | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 2c87dda4c..4d15e28a5 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -33,7 +33,6 @@ import tools.debugger.entities.DynamicScopeType; import tools.replay.actors.ActorExecutionTrace; import tools.replay.nodes.TraceActorContextNode; -import tools.snapshot.SnapshotBuffer; /** @@ -295,28 +294,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); - long loc = firstMessage.serialize(sb); - if (loc != -1) { - sb.getOwner().addMessageLocation( - ((TracingActor) actor).getSnapshotRecord().getMessageIdentifier(), - sb.calculateReference(loc)); - } - } execute(firstMessage, currentThread, dbg); if (size > 1) { for (EventualMessage msg : mailboxExtension) { - if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { - long loc = msg.serialize(currentThread.getSnapshotBuffer()); - if (loc != -1) { - currentThread.addMessageLocation( - ((TracingActor) actor).getSnapshotRecord().getMessageIdentifier(), - currentThread.getSnapshotBuffer().calculateReference(loc)); - } - } execute(msg, currentThread, dbg); } } diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java index 94fdf661a..8218ba97e 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -9,13 +9,16 @@ import som.VM; import som.interpreter.SArguments; import som.interpreter.SomLanguage; +import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.SPromise.SResolver; import som.vm.VmSettings; 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.SnapshotBuffer; import tools.snapshot.nodes.MessageSerializationNode; import tools.snapshot.nodes.MessageSerializationNodeFactory; @@ -57,6 +60,19 @@ 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) { + SnapshotBuffer sb = currentThread.getSnapshotBuffer(); + sb.getRecord().handleTodos(sb); + long loc = msg.serialize(sb); + if (loc != -1) { + sb.getOwner().addMessageLocation( + ((TracingActor) msg.getTarget()).getSnapshotRecord().getMessageIdentifier(), + sb.calculateReference(loc)); + } + } + boolean haltOnResolver; boolean haltOnResolution; From 9c77462b0427e88094e9356ce58945006e25e566 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Mon, 17 Dec 2018 19:03:25 +0100 Subject: [PATCH 26/80] Pass thread object explicitly to recordActorContext Signed-off-by: Stefan Marr --- src/som/interpreter/actors/Actor.java | 2 +- src/tools/replay/actors/ActorExecutionTrace.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index 4d15e28a5..2cdd09471 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -271,7 +271,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); } 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); } From 2dbb024a89b66db4d582d368d660bd6090acb07c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Mon, 17 Dec 2018 22:49:34 +0100 Subject: [PATCH 27/80] Added @TruffleBoundary to some map-related methods Signed-off-by: Stefan Marr --- src/tools/snapshot/SnapshotRecord.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 6f1d456cd..475ce4abe 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -4,6 +4,8 @@ import org.graalvm.collections.EconomicMap; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; + import som.interpreter.Types; import som.interpreter.actors.EventualMessage.PromiseMessage; import tools.concurrency.TracingActors.TracingActor; @@ -45,16 +47,19 @@ public long getMessageIdentifier() { /** * 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); @@ -63,6 +68,7 @@ public long getObjectPointer(final Object o) { "Cannot point to unserialized Objects, you are missing a serialization call: " + o); } + @TruffleBoundary public void addObjectEntry(final Object o, final long offset) { synchronized (entries) { entries.put(o, offset); From d6756410c4101ce991da7f5ad8eb57837225a883 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Mon, 17 Dec 2018 22:50:19 +0100 Subject: [PATCH 28/80] Added comment on Types.getClassOf to link it to ClassPrim Signed-off-by: Stefan Marr --- src/som/interpreter/Types.java | 5 +++++ 1 file changed, 5 insertions(+) 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"); From 4800d9d07bca42a82d892175363fac2cad7d062c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 10:05:26 +0100 Subject: [PATCH 29/80] Use castExact to avoid issue with non-leaf class cast Signed-off-by: Stefan Marr --- src/tools/snapshot/SnapshotBuffer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/snapshot/SnapshotBuffer.java b/src/tools/snapshot/SnapshotBuffer.java index b6faca40b..8483e15a4 100644 --- a/src/tools/snapshot/SnapshotBuffer.java +++ b/src/tools/snapshot/SnapshotBuffer.java @@ -1,5 +1,7 @@ package tools.snapshot; +import com.oracle.truffle.api.CompilerDirectives; + import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.EventualMessage; import som.vm.VmSettings; @@ -28,7 +30,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() { From a41e852dc47c14484f34d80eaa800c14516693c1 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 10:07:17 +0100 Subject: [PATCH 30/80] MessageSerializationNode: extract processArguments Signed-off-by: Stefan Marr --- .../nodes/MessageSerializationNode.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index d0190bb0c..8cd7cb06f 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -125,9 +125,7 @@ protected long doDirectMessage(final DirectMessage dm, final SnapshotBuffer 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 @@ -143,9 +141,7 @@ protected long doDirectMessageNoResolver(final DirectMessage dm, final SnapshotB base, sb); base += COMMONALITY_BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = "dm.getResolver() != null") @@ -167,9 +163,7 @@ protected long doCallbackMessage(final PromiseCallbackMessage dm, final Snapshot sb.putLongAt(base + COMMONALITY_BYTES + Long.BYTES, sb.getRecord().getObjectPointer(prom)); base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization @@ -189,6 +183,11 @@ protected long doCallbackMessageNoResolver(final PromiseCallbackMessage dm, sb.putLongAt(base + COMMONALITY_BYTES, sb.getRecord().getObjectPointer(prom)); 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); @@ -218,9 +217,7 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES + Long.BYTES, fsender.getActorId()); base += COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @Specialization(guards = "dm.isDelivered()") @@ -245,9 +242,7 @@ protected long doPromiseMessageNoResolver(final PromiseSendMessage dm, sb.putIntAt(base + COMMONALITY_BYTES + Long.BYTES, fsender.getActorId()); 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"}) @@ -268,9 +263,7 @@ protected long doUndeliveredPromiseMessage(final PromiseSendMessage dm, 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(guards = "!dm.isDelivered()") @@ -286,9 +279,7 @@ protected long doUndeliveredPromiseMessageNoResolver(final PromiseSendMessage dm (TracingActor) dm.getSender(), base, sb); base += COMMONALITY_BYTES; - doArguments(args, base, sb); - - return sb.calculateReference(start); + return processArguments(sb, args, base, start); } @TruffleBoundary From 75e89c06e8a30a791a12d0ad4afdd456e7fdda99 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 10:10:00 +0100 Subject: [PATCH 31/80] Remove EventualMessage.serialize and do directly in RRN This is to be more explicit about the relations between the nodes and possible specializations. Signed-off-by: Stefan Marr --- src/som/interpreter/actors/EventualMessage.java | 15 --------------- src/som/interpreter/actors/ReceivedRootNode.java | 14 +++++++++++++- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index 3ee32f7b9..3dffe2259 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -79,21 +79,6 @@ 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 - if (sb.getRecord().containsObject(this)) { - return sb.getRecord().getObjectPointer(this); - } - return rm.getSerializer().execute(this, sb); - } else { - // need to be careful, might interfere with promise serialization... - return -1; - } - } - public long forceSerialize(final SnapshotBuffer sb) { if (sb.getRecord().containsObject(this)) { return sb.getRecord().getObjectPointer(this); diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java index 8218ba97e..e88d509bf 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -65,7 +65,19 @@ public final Object execute(final VirtualFrame frame) { if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { SnapshotBuffer sb = currentThread.getSnapshotBuffer(); sb.getRecord().handleTodos(sb); - long loc = msg.serialize(sb); + + long loc; + if (sb.needsToBeSnapshot(msg.getMessageId())) { + // Not sure if this is optimized, worst case need to duplicate this for all messages + if (sb.getRecord().containsObject(msg)) { + return sb.getRecord().getObjectPointer(msg); + } + loc = serializer.execute(msg, sb); + } else { + // need to be careful, might interfere with promise serialization... + loc = -1; + } + if (loc != -1) { sb.getOwner().addMessageLocation( ((TracingActor) msg.getTarget()).getSnapshotRecord().getMessageIdentifier(), From 73f2b4265b11a3852ec5c9f8d4096db6b5cb5310 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 12:07:39 +0100 Subject: [PATCH 32/80] Optimize MessageSerializationNode - make selector constant - gives constant number of arguments - allows us to specialize on type of arguments - allow initializing serializerFactory and consequently serializationRoot as null, since we do not have a selector to create a factory for the messageClass Signed-off-by: Stefan Marr --- .../interpreter/actors/ReceivedMessage.java | 6 +- .../interpreter/actors/ReceivedRootNode.java | 6 +- .../objectstorage/ClassFactory.java | 3 +- src/som/primitives/ObjectPrims.java | 3 + src/som/primitives/actors/PromisePrims.java | 2 +- src/som/vm/ObjectSystem.java | 3 +- src/som/vmobjects/SClass.java | 10 ++- .../nodes/MessageSerializationNode.java | 82 +++++++++++++------ 8 files changed, 77 insertions(+), 38 deletions(-) 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 e88d509bf..99b462de4 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -12,6 +12,7 @@ import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.SPromise.SResolver; import som.vm.VmSettings; +import som.vmobjects.SSymbol; import tools.concurrency.KomposTrace; import tools.concurrency.TracingActors.TracingActor; import tools.debugger.WebDebugger; @@ -36,7 +37,8 @@ public abstract class ReceivedRootNode extends RootNode { private final SourceSection sourceSection; 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(); @@ -47,7 +49,7 @@ protected ReceivedRootNode(final SomLanguage language, } this.sourceSection = sourceSection; if (VmSettings.SNAPSHOTS_ENABLED) { - serializer = MessageSerializationNodeFactory.create(); + serializer = MessageSerializationNodeFactory.create(selector); } else { serializer = null; } diff --git a/src/som/interpreter/objectstorage/ClassFactory.java b/src/som/interpreter/objectstorage/ClassFactory.java index 8305308c1..1fb2e5a57 100644 --- a/src/som/interpreter/objectstorage/ClassFactory.java +++ b/src/som/interpreter/objectstorage/ClassFactory.java @@ -143,7 +143,8 @@ public MixinDefinition getMixinDefinition() { } public NodeFactory getSerializerFactory() { - return this.serializerFactory; + // serializerFactory can be null for Classes.messageClass + return serializerFactory; } /** diff --git a/src/som/primitives/ObjectPrims.java b/src/som/primitives/ObjectPrims.java index 6caf600ac..448430162 100644 --- a/src/som/primitives/ObjectPrims.java +++ b/src/som/primitives/ObjectPrims.java @@ -70,6 +70,9 @@ 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 doSAbstractObject(final SAbstractObject receiver) { return receiver.getSOMClass(); 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 f8d894945..02179c372 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -54,7 +54,6 @@ 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.ObjectSerializationNodesFactory.UninitializedObjectSerializationNodeFactory; import tools.snapshot.nodes.PrimitiveSerializationNodesFactory.BooleanSerializationNodeFactory; @@ -427,7 +426,7 @@ public SObjectWithoutFields initialize() { // these classes are not exposed in Newspeak directly, and thus, do not yet have a class // factory - setDummyClassFactory(Classes.messageClass, MessageSerializationNodeFactory.getInstance()); + setDummyClassFactory(Classes.messageClass, null); // MessageSerializationNodeFactory.getInstance()); setDummyClassFactory(Classes.frameClass, UninitializedObjectSerializationNodeFactory.getInstance()); setDummyClassFactory(Classes.methodClass, diff --git a/src/som/vmobjects/SClass.java b/src/som/vmobjects/SClass.java index d56952be4..9b476893d 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; @@ -212,9 +213,11 @@ public void initializeStructure(final MixinDefinition mixinDef, SnapshotBackend.registerClassEnclosure(this); } - this.serializationRoot = - new SerializerRootNode(classFactory.getSerializerFactory().createNode(this)); - + NodeFactory factory = + classFactory.getSerializerFactory(); + if (factory != null) { + this.serializationRoot = new SerializerRootNode(factory.createNode(this)); + } } // assert instanceClassGroup != null || !ObjectSystem.isInitialized(); @@ -383,6 +386,7 @@ public void serialize(final Object o, final SnapshotBuffer sb) { } public AbstractSerializationNode getSerializer() { + assert serializationRoot != null : "Unclear what's wrong, but MessageSerializationNode can be null for Classes.messageClass"; return serializationRoot.getSerializer(); } diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 8cd7cb06f..adaa91856 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -1,5 +1,6 @@ package tools.snapshot.nodes; +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.dsl.GenerateNodeFactory; @@ -7,7 +8,6 @@ import som.Output; import som.interpreter.SomLanguage; -import som.interpreter.Types; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage; import som.interpreter.actors.EventualMessage.DirectMessage; @@ -17,6 +17,8 @@ import som.interpreter.actors.EventualSendNode; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.SResolver; +import som.primitives.ObjectPrims.ClassPrim; +import som.primitives.ObjectPrimsFactory.ClassPrimFactory; import som.primitives.actors.PromisePrims; import som.vm.constants.Classes; import som.vm.constants.Nil; @@ -33,15 +35,25 @@ @GenerateNodeFactory public abstract class MessageSerializationNode extends AbstractSerializationNode { - public MessageSerializationNode(final SClass clazz) { + protected static final int COMMONALITY_BYTES = 7; + + private final SSymbol selector; + + @CompilationFinal(dimensions = 1) private final ClassPrim[] argumentClasses; + + public MessageSerializationNode(final SClass clazz, final SSymbol selector) { super(clazz); - } + this.selector = selector; + this.argumentClasses = new ClassPrim[selector.getNumberOfSignatureArguments()]; - public MessageSerializationNode() { - super(Classes.messageClass); + for (int i = 0; i < argumentClasses.length; i++) { + this.argumentClasses[i] = ClassPrimFactory.create(null); + } } - protected static final int COMMONALITY_BYTES = 7; + public MessageSerializationNode(final SSymbol selector) { + this(Classes.messageClass, selector); + } public enum MessageType { DirectMessage, CallbackMessage, PromiseMessage, UndeliveredPromiseMessage, DirectMessageNR, @@ -80,8 +92,8 @@ protected final void doArguments(final Object[] args, final int base, 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++) { + sb.putByteAt(base, (byte) argumentClasses.length); + for (int i = 0; i < argumentClasses.length; i++) { if (args[i] == null) { if (!sb.getRecord().containsObjectUnsync(Nil.nilObject)) { Classes.nilClass.serialize(Nil.nilObject, sb); @@ -90,7 +102,9 @@ protected final void doArguments(final Object[] args, final int base, sb.getRecord().getObjectPointer(Nil.nilObject)); } else { if (!sb.getRecord().containsObjectUnsync(args[i])) { - Types.getClassOf(args[i]).serialize(args[i], sb); + // TODO: can we specialize this on the ClassGroup/Factory? + SClass clazz = argumentClasses[i].executeEvaluated(args[i]); + clazz.serialize(args[i], sb); } sb.putLongAt((base + 1) + i * Long.BYTES, sb.getRecord().getObjectPointer(args[i])); } @@ -114,11 +128,13 @@ 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 payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.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); @@ -132,12 +148,13 @@ protected long doDirectMessage(final DirectMessage dm, final SnapshotBuffer sb) 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 payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.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; @@ -150,11 +167,14 @@ 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 payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 + + (argumentClasses.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); @@ -172,11 +192,13 @@ 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 payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.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); @@ -202,11 +224,13 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES + 1 - + (args.length * Long.BYTES); + + (argumentClasses.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); @@ -229,11 +253,13 @@ protected long doPromiseMessageNoResolver(final PromiseSendMessage dm, Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Integer.BYTES + 1 - + (args.length * Long.BYTES); + + (argumentClasses.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.PromiseMessageNR, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.PromiseMessageNR, selector, (TracingActor) dm.getSender(), base, sb); serializePromise(prom, sb); @@ -252,13 +278,15 @@ 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 payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.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; @@ -271,11 +299,13 @@ protected long doUndeliveredPromiseMessageNoResolver(final PromiseSendMessage dm final SnapshotBuffer sb) { Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + 1 + (args.length * Long.BYTES); + int payload = COMMONALITY_BYTES + 1 + (argumentClasses.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; - doCommonalities(MessageType.UndeliveredPromiseMessageNR, dm.getSelector(), + assert dm.getSelector() == selector; + + doCommonalities(MessageType.UndeliveredPromiseMessageNR, selector, (TracingActor) dm.getSender(), base, sb); base += COMMONALITY_BYTES; From b386712a94ddad615a34376b1dcd16333caa08ff Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 16:05:27 +0100 Subject: [PATCH 33/80] Turn ClassPrim into optimized node to get a class object Signed-off-by: Stefan Marr --- .../interpreter/actors/ReceivedRootNode.java | 8 ++++- src/som/primitives/ObjectPrims.java | 30 +++++++++++++++++++ src/tools/snapshot/SnapshotBackend.java | 9 +++++- src/tools/snapshot/SnapshotRecord.java | 7 +++-- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java index 99b462de4..c60044228 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -11,6 +11,8 @@ import som.interpreter.SomLanguage; import som.interpreter.actors.Actor.ActorProcessingThread; 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; @@ -32,6 +34,8 @@ 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; @@ -50,8 +54,10 @@ protected ReceivedRootNode(final SomLanguage language, this.sourceSection = sourceSection; if (VmSettings.SNAPSHOTS_ENABLED) { serializer = MessageSerializationNodeFactory.create(selector); + classPrim = ClassPrimFactory.create(null); } else { serializer = null; + classPrim = null; } } @@ -66,7 +72,7 @@ public final Object execute(final VirtualFrame frame) { if (VmSettings.SNAPSHOTS_ENABLED && !VmSettings.TEST_SNAPSHOTS) { SnapshotBuffer sb = currentThread.getSnapshotBuffer(); - sb.getRecord().handleTodos(sb); + sb.getRecord().handleTodos(sb, classPrim); long loc; if (sb.needsToBeSnapshot(msg.getMessageId())) { diff --git a/src/som/primitives/ObjectPrims.java b/src/som/primitives/ObjectPrims.java index 448430162..22cd4f58f 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; @@ -73,8 +76,35 @@ 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"); + System.out.println(receiver.getSOMClass()); return receiver.getSOMClass(); } diff --git a/src/tools/snapshot/SnapshotBackend.java b/src/tools/snapshot/SnapshotBackend.java index 0334fe776..c32943696 100644 --- a/src/tools/snapshot/SnapshotBackend.java +++ b/src/tools/snapshot/SnapshotBackend.java @@ -20,6 +20,7 @@ import org.graalvm.collections.MapCursor; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import som.VM; import som.compiler.MixinDefinition; @@ -29,6 +30,8 @@ import som.interpreter.actors.SPromise; 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; @@ -293,10 +296,12 @@ public static void registerSnapshotBuffer(final SnapshotBuffer sb, messages.add(messageLocations); } + @TruffleBoundary public static void addUnfinishedTodo(final SnapshotRecord sr) { unfinishedSerializations.put(sr, 0); } + @TruffleBoundary public static void removeTodo(final SnapshotRecord sr) { unfinishedSerializations.remove(sr); } @@ -317,6 +322,8 @@ public static TracingActor getCurrentActor() { /** * Persist the current snapshot to a file. */ + private static final ClassPrim classPrim = ClassPrimFactory.create(null); + public static void writeSnapshot() { if (buffers.size() == 0) { return; @@ -337,7 +344,7 @@ public static void writeSnapshot() { assert sr.owner != null; unfinishedSerializations.remove(sr); buffer.owner.setCurrentActorSnapshot(sr.owner); - sr.handleTodos(buffer); + sr.handleTodos(buffer, classPrim); } } diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 475ce4abe..89829b6fb 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -8,6 +8,8 @@ import som.interpreter.Types; import som.interpreter.actors.EventualMessage.PromiseMessage; +import som.primitives.ObjectPrims.ClassPrim; +import som.vmobjects.SClass; import tools.concurrency.TracingActors.TracingActor; @@ -75,7 +77,7 @@ public void addObjectEntry(final Object o, final long offset) { } } - public void handleTodos(final SnapshotBuffer sb) { + public void handleTodos(final SnapshotBuffer sb, final ClassPrim classPrim) { // SnapshotBackend.removeTodo(this); while (!externalReferences.isEmpty()) { FarRefTodo frt = externalReferences.poll(); @@ -86,7 +88,8 @@ public void handleTodos(final SnapshotBuffer sb) { if (frt.target instanceof PromiseMessage) { ((PromiseMessage) frt.target).forceSerialize(sb); } else { - Types.getClassOf(frt.target).serialize(frt.target, sb); + SClass clazz = classPrim.executeEvaluated(frt.target); + clazz.serialize(frt.target, sb); } } frt.resolve(getObjectPointer(frt.target)); From 4d9275e4e7d96202c724879c85237fc7825f1dad Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 16:05:42 +0100 Subject: [PATCH 34/80] Profile the class of a message in ReceivedRootNode Signed-off-by: Stefan Marr --- src/som/interpreter/actors/ReceivedRootNode.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java index c60044228..3289ef399 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -4,6 +4,7 @@ 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; @@ -40,6 +41,8 @@ public abstract class ReceivedRootNode extends RootNode { protected final WebDebugger dbg; private final SourceSection sourceSection; + private final ValueProfile msgClass; + protected ReceivedRootNode(final SomLanguage language, final SourceSection sourceSection, final FrameDescriptor frameDescriptor, final SSymbol selector) { @@ -55,9 +58,11 @@ protected ReceivedRootNode(final SomLanguage language, if (VmSettings.SNAPSHOTS_ENABLED) { serializer = MessageSerializationNodeFactory.create(selector); classPrim = ClassPrimFactory.create(null); + msgClass = ValueProfile.createClassProfile(); } else { serializer = null; classPrim = null; + msgClass = null; } } @@ -88,7 +93,7 @@ public final Object execute(final VirtualFrame frame) { if (loc != -1) { sb.getOwner().addMessageLocation( - ((TracingActor) msg.getTarget()).getSnapshotRecord().getMessageIdentifier(), + ((TracingActor) msgClass.profile(msg).getTarget()).getSnapshotRecord().getMessageIdentifier(), sb.calculateReference(loc)); } } From d2a64e56c94f3b946d45b6eeeefe5b62034287bc Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 16:18:03 +0100 Subject: [PATCH 35/80] Added helper to be able to put map.get behind boundary Signed-off-by: Stefan Marr --- src/tools/snapshot/SnapshotRecord.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tools/snapshot/SnapshotRecord.java b/src/tools/snapshot/SnapshotRecord.java index 89829b6fb..6613f5a96 100644 --- a/src/tools/snapshot/SnapshotRecord.java +++ b/src/tools/snapshot/SnapshotRecord.java @@ -6,7 +6,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import som.interpreter.Types; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.primitives.ObjectPrims.ClassPrim; import som.vmobjects.SClass; @@ -109,10 +108,7 @@ public void handleTodos(final SnapshotBuffer sb, final ClassPrim classPrim) { */ 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.putLongAt(destination, l); @@ -126,10 +122,7 @@ public void farReference(final Object o, final SnapshotBuffer other, public void farReferenceMessage(final PromiseMessage pm, final SnapshotBuffer other, final int destination) { - Long l; - synchronized (entries) { - l = entries.get(pm); - } + Long l = getEntrySynced(pm); if (l != null) { other.putLongAt(destination, l); @@ -141,6 +134,15 @@ public void farReferenceMessage(final PromiseMessage pm, final SnapshotBuffer ot } } + @TruffleBoundary + private Long getEntrySynced(final Object o) { + Long l; + synchronized (entries) { + l = entries.get(o); + } + return l; + } + public static final class FarRefTodo { private final SnapshotBuffer referer; private final int referenceOffset; From e59e13310fc848b1e651f1df7dc5d0c67014cab1 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 16:18:22 +0100 Subject: [PATCH 36/80] Explode the doArguments loop Signed-off-by: Stefan Marr --- src/tools/snapshot/nodes/MessageSerializationNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index adaa91856..1dd46927c 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -5,6 +5,7 @@ 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.Output; import som.interpreter.SomLanguage; @@ -83,8 +84,7 @@ 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) { From b3a5079de1ffb5bd03a1f4282c5cca340bc10658 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 17:50:30 +0100 Subject: [PATCH 37/80] Make SnapshotRecord MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SnapshotRecord is only changed for testing (snapshotClone:). Thus, it’s safe to mark it compilation final. Signed-off-by: Stefan Marr --- src/tools/concurrency/TracingActors.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/concurrency/TracingActors.java b/src/tools/concurrency/TracingActors.java index c1d151b92..059bdc42f 100644 --- a/src/tools/concurrency/TracingActors.java +++ b/src/tools/concurrency/TracingActors.java @@ -10,6 +10,7 @@ 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; @@ -36,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. From f43a3aab79427ffbfb352d00090e16b52e723966 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 18 Dec 2018 17:54:08 +0100 Subject: [PATCH 38/80] Restructure MessageSerializationNode.doArguments() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - avoid run time dependency on args array - have local variable for record, which shouldn’t change - use early return to avoid extra nesting level in an already complex method Signed-off-by: Stefan Marr --- .../nodes/MessageSerializationNode.java | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 1dd46927c..8b0003be1 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -29,6 +29,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; @@ -47,6 +48,10 @@ public MessageSerializationNode(final SClass clazz, final SSymbol selector) { this.selector = selector; this.argumentClasses = new ClassPrim[selector.getNumberOfSignatureArguments()]; + assert argumentClasses.length < 2 + * Byte.MAX_VALUE : "We assume the number of args is reasonable, but was huge: " + + argumentClasses.length; + for (int i = 0; i < argumentClasses.length; i++) { this.argumentClasses[i] = ClassPrimFactory.create(null); } @@ -87,27 +92,29 @@ public static MessageType getMessageType(final byte ordinal) { @ExplodeLoop protected final void doArguments(final Object[] args, final int base, final SnapshotBuffer sb) { + assert argumentClasses.length == args.length; + + if (argumentClasses.length <= 0) { + return; + } + + // special case for callback message + sb.putByteAt(base, (byte) argumentClasses.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) argumentClasses.length); - for (int i = 0; i < argumentClasses.length; i++) { - if (args[i] == null) { - if (!sb.getRecord().containsObjectUnsync(Nil.nilObject)) { - Classes.nilClass.serialize(Nil.nilObject, sb); - } - sb.putLongAt((base + 1) + i * Long.BYTES, - sb.getRecord().getObjectPointer(Nil.nilObject)); - } else { - if (!sb.getRecord().containsObjectUnsync(args[i])) { - // TODO: can we specialize this on the ClassGroup/Factory? - SClass clazz = argumentClasses[i].executeEvaluated(args[i]); - clazz.serialize(args[i], sb); - } - sb.putLongAt((base + 1) + i * Long.BYTES, sb.getRecord().getObjectPointer(args[i])); + for (int i = 0; i < argumentClasses.length; i++) { + SnapshotRecord record = sb.getRecord(); + if (args[i] == 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 (!record.containsObjectUnsync(args[i])) { + // TODO: can we specialize this on the ClassGroup/Factory? + SClass clazz = argumentClasses[i].executeEvaluated(args[i]); + clazz.serialize(args[i], sb); } + sb.putLongAt((base + 1) + i * Long.BYTES, record.getObjectPointer(args[i])); } } } From 960b4508110ba0c72911e67b460fe1e7ca519657 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 19 Dec 2018 18:25:21 +0100 Subject: [PATCH 39/80] Use CachedSerializationNode in MessageSerializationNode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since classes are merely id, but not the structure, it’s less stable to specialize on classes. Each new object with inner classes will generate new class objects for the inner classes. Thus, the specialization would need to adapt. Therefore, avoid using the argumentClasses in MSN. I am now using the CachedSerializationNode, which also still needs to be adapted though. Signed-off-by: Stefan Marr --- .../nodes/MessageSerializationNode.java | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 8b0003be1..95d44145e 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -1,6 +1,6 @@ package tools.snapshot.nodes; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -18,8 +18,6 @@ import som.interpreter.actors.EventualSendNode; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.SResolver; -import som.primitives.ObjectPrims.ClassPrim; -import som.primitives.ObjectPrimsFactory.ClassPrimFactory; import som.primitives.actors.PromisePrims; import som.vm.constants.Classes; import som.vm.constants.Nil; @@ -41,20 +39,17 @@ public abstract class MessageSerializationNode extends AbstractSerializationNode private final SSymbol selector; - @CompilationFinal(dimensions = 1) private final ClassPrim[] argumentClasses; + @Children private final CachedSerializationNode[] serializationNodes; public MessageSerializationNode(final SClass clazz, final SSymbol selector) { super(clazz); this.selector = selector; - this.argumentClasses = new ClassPrim[selector.getNumberOfSignatureArguments()]; + this.serializationNodes = + new CachedSerializationNode[selector.getNumberOfSignatureArguments()]; - assert argumentClasses.length < 2 + assert serializationNodes.length < 2 * Byte.MAX_VALUE : "We assume the number of args is reasonable, but was huge: " - + argumentClasses.length; - - for (int i = 0; i < argumentClasses.length; i++) { - this.argumentClasses[i] = ClassPrimFactory.create(null); - } + + serializationNodes.length; } public MessageSerializationNode(final SSymbol selector) { @@ -92,29 +87,38 @@ public static MessageType getMessageType(final byte ordinal) { @ExplodeLoop protected final void doArguments(final Object[] args, final int base, final SnapshotBuffer sb) { - assert argumentClasses.length == args.length; + assert serializationNodes.length == args.length; - if (argumentClasses.length <= 0) { + if (serializationNodes.length <= 0) { return; } // special case for callback message - sb.putByteAt(base, (byte) argumentClasses.length); + sb.putByteAt(base, (byte) serializationNodes.length); + + for (int i = 0; i < serializationNodes.length; i++) { + final Object obj = args[i]; - for (int i = 0; i < argumentClasses.length; i++) { SnapshotRecord record = sb.getRecord(); - if (args[i] == null) { + 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 (!record.containsObjectUnsync(args[i])) { + if (!record.containsObjectUnsync(obj)) { // TODO: can we specialize this on the ClassGroup/Factory? - SClass clazz = argumentClasses[i].executeEvaluated(args[i]); - clazz.serialize(args[i], sb); + + if (!sb.getRecord().containsObjectUnsync(obj)) { + if (serializationNodes[i] == null) { + // initialize the node + CompilerDirectives.transferToInterpreterAndInvalidate(); + serializationNodes[i] = CachedSerializationNodeFactory.create(obj); + } + serializationNodes[i].serialize(obj, sb); + } } - sb.putLongAt((base + 1) + i * Long.BYTES, record.getObjectPointer(args[i])); + sb.putLongAt((base + 1) + i * Long.BYTES, record.getObjectPointer(obj)); } } } @@ -135,7 +139,8 @@ protected long doDirectMessage(final DirectMessage dm, final SnapshotBuffer sb) SResolver resolver = dm.getResolver(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.length * Long.BYTES); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -155,7 +160,8 @@ protected long doDirectMessage(final DirectMessage dm, final SnapshotBuffer sb) protected long doDirectMessageNoResolver(final DirectMessage dm, final SnapshotBuffer sb) { Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.length * Long.BYTES); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -175,7 +181,7 @@ protected long doCallbackMessage(final PromiseCallbackMessage dm, final Snapshot Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + 1 - + (argumentClasses.length * Long.BYTES); + + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -199,7 +205,8 @@ protected long doCallbackMessageNoResolver(final PromiseCallbackMessage dm, SPromise prom = dm.getPromise(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.length * Long.BYTES); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -231,7 +238,7 @@ protected long doPromiseMessage(final PromiseSendMessage dm, final SnapshotBuffe Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Long.BYTES + Integer.BYTES + 1 - + (argumentClasses.length * Long.BYTES); + + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -260,7 +267,7 @@ protected long doPromiseMessageNoResolver(final PromiseSendMessage dm, Object[] args = dm.getArgs(); int payload = COMMONALITY_BYTES + Long.BYTES + Integer.BYTES + 1 - + (argumentClasses.length * Long.BYTES); + + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -285,7 +292,8 @@ protected long doUndeliveredPromiseMessage(final PromiseSendMessage dm, SResolver resolver = dm.getResolver(); Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + Long.BYTES + 1 + (argumentClasses.length * Long.BYTES); + int payload = + COMMONALITY_BYTES + Long.BYTES + 1 + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; @@ -306,7 +314,7 @@ protected long doUndeliveredPromiseMessageNoResolver(final PromiseSendMessage dm final SnapshotBuffer sb) { Object[] args = dm.getArgs(); - int payload = COMMONALITY_BYTES + 1 + (argumentClasses.length * Long.BYTES); + int payload = COMMONALITY_BYTES + 1 + (serializationNodes.length * Long.BYTES); int base = sb.addMessage(payload, dm); long start = base - SnapshotBuffer.CLASS_ID_SIZE; From 4cb4ff576b3eb5eb1ec85ec48b946d008db987ff Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 19 Dec 2018 18:25:47 +0100 Subject: [PATCH 40/80] Added transferToInterpreterAndInvalidate to OSN.serialize Signed-off-by: Stefan Marr --- src/tools/snapshot/nodes/ObjectSerializationNodes.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index 228d11b1a..fa3c06b52 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -10,6 +10,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import som.compiler.MixinDefinition.SlotDefinition; +import som.interpreter.TruffleCompiler; import som.interpreter.nodes.dispatch.AbstractDispatchNode; import som.interpreter.nodes.dispatch.CachedSlotRead; import som.interpreter.nodes.dispatch.CachedSlotRead.SlotAccess; @@ -124,6 +125,8 @@ protected UninitializedObjectSerializationNode(final SClass clazz) { @Specialization public void serialize(final SObjectWithClass o, final SnapshotBuffer sb) { + TruffleCompiler.transferToInterpreterAndInvalidate( + "Initialize ObjectSerializationNode."); if (o instanceof SObject) { replace(SObjectSerializationNodeFactory.create(clazz, createReadNodes((SObject) o))).serialize((SObject) o, sb); From d9442d0718870f96cecc3a3b2fdeec0fb673fd69 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 20 Dec 2018 13:17:06 +0100 Subject: [PATCH 41/80] Fix lint issue Signed-off-by: Stefan Marr --- src/tools/snapshot/deserialization/SnapshotParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/snapshot/deserialization/SnapshotParser.java b/src/tools/snapshot/deserialization/SnapshotParser.java index ab1bf741e..349c22588 100644 --- a/src/tools/snapshot/deserialization/SnapshotParser.java +++ b/src/tools/snapshot/deserialization/SnapshotParser.java @@ -231,7 +231,7 @@ private class MessageLocation implements Comparable { final int msgNo; final long location; - public MessageLocation(final int msgNo, final long location) { + MessageLocation(final int msgNo, final long location) { this.msgNo = msgNo; this.location = location; } From 69cf83d777a641e1437560c34a7ad161264ff76f Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 20 Dec 2018 13:17:25 +0100 Subject: [PATCH 42/80] Update dym expected results Signed-off-by: Stefan Marr --- tests/dym/expected-results.tar.bz2 | Bin 139776 -> 137204 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/dym/expected-results.tar.bz2 b/tests/dym/expected-results.tar.bz2 index 26e0eb051ad673931641354fb94d6baabb98fa8a..b7134c3644db42b08aa9e4ec154f3fd158497862 100644 GIT binary patch literal 137204 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