Skip to content

Commit 961a320

Browse files
Add suspended cache validation; Add possibility to remove old variables from cache; some improvements
1 parent 2252f47 commit 961a320

File tree

8 files changed

+375
-71
lines changed

8 files changed

+375
-71
lines changed

Diff for: src/main/kotlin/org/jetbrains/kotlinx/jupyter/message_types.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,8 @@ class ListErrorsReply(
578578
class SerializationRequest(
579579
val cellId: Int,
580580
val descriptorsState: Map<String, SerializedVariablesState>,
581-
val topLevelDescriptorName: String = ""
581+
val topLevelDescriptorName: String = "",
582+
val pathToDescriptor: List<String> = emptyList()
582583
) : MessageContent()
583584

584585
@Serializable

Diff for: src/main/kotlin/org/jetbrains/kotlinx/jupyter/protocol.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,11 @@ fun JupyterConnection.Socket.shellMessagesHandler(msg: Message, repl: ReplForJup
315315

316316
val messageContent = getVariablesDescriptorsFromJson(data)
317317
GlobalScope.launch(Dispatchers.Default) {
318-
repl.serializeVariables(messageContent.topLevelDescriptorName, messageContent.descriptorsState) { result ->
318+
repl.serializeVariables(
319+
messageContent.topLevelDescriptorName,
320+
messageContent.descriptorsState,
321+
messageContent.pathToDescriptor
322+
) { result ->
319323
sendWrapped(msg, makeReplyMessage(msg, MessageType.COMM_OPEN, content = result))
320324
}
321325
}
@@ -337,7 +341,7 @@ fun JupyterConnection.Socket.shellMessagesHandler(msg: Message, repl: ReplForJup
337341
is SerializationRequest -> {
338342
GlobalScope.launch(Dispatchers.Default) {
339343
if (content.topLevelDescriptorName.isNotEmpty()) {
340-
repl.serializeVariables(content.topLevelDescriptorName, content.descriptorsState) { result ->
344+
repl.serializeVariables(content.topLevelDescriptorName, content.descriptorsState, content.pathToDescriptor) { result ->
341345
sendWrapped(msg, makeReplyMessage(msg, MessageType.SERIALIZATION_REPLY, content = result))
342346
}
343347
} else {

Diff for: src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl.kt

+27-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import jupyter.kotlin.DependsOn
55
import jupyter.kotlin.KotlinContext
66
import jupyter.kotlin.KotlinKernelHostProvider
77
import jupyter.kotlin.Repository
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.GlobalScope
10+
import kotlinx.coroutines.launch
11+
import org.jetbrains.annotations.TestOnly
812
import org.jetbrains.kotlin.config.KotlinCompilerVersion
913
import org.jetbrains.kotlinx.jupyter.api.Code
1014
import org.jetbrains.kotlinx.jupyter.api.ExecutionCallback
@@ -48,6 +52,7 @@ import org.jetbrains.kotlinx.jupyter.repl.CellExecutor
4852
import org.jetbrains.kotlinx.jupyter.repl.CompletionResult
4953
import org.jetbrains.kotlinx.jupyter.repl.ContextUpdater
5054
import org.jetbrains.kotlinx.jupyter.repl.EvalResult
55+
import org.jetbrains.kotlinx.jupyter.repl.EvalResultEx
5156
import org.jetbrains.kotlinx.jupyter.repl.InternalEvaluator
5257
import org.jetbrains.kotlinx.jupyter.repl.KotlinCompleter
5358
import org.jetbrains.kotlinx.jupyter.repl.ListErrorsResult
@@ -120,7 +125,8 @@ interface ReplForJupyter {
120125

121126
suspend fun serializeVariables(cellId: Int, descriptorsState: Map<String, SerializedVariablesState>, callback: (SerializationReply) -> Unit)
122127

123-
suspend fun serializeVariables(topLevelVarName: String, descriptorsState: Map<String, SerializedVariablesState>, callback: (SerializationReply) -> Unit)
128+
suspend fun serializeVariables(topLevelVarName: String, descriptorsState: Map<String, SerializedVariablesState>, pathToDescriptor: List<String> = emptyList(),
129+
callback: (SerializationReply) -> Unit)
124130

125131
val homeDir: File?
126132

@@ -191,7 +197,7 @@ class ReplForJupyterImpl(
191197

192198
override val variablesSerializer = VariablesSerializer()
193199

194-
private val librariesScanner = LibrariesScanner(notebook)
200+
val librariesScanner = LibrariesScanner(notebook)
195201
private val resourcesProcessor = LibraryResourcesProcessorImpl()
196202

197203
override var outputConfig
@@ -347,7 +353,7 @@ class ReplForJupyterImpl(
347353
)
348354

349355
private var evalContextEnabled = false
350-
private fun withEvalContext(action: () -> EvalResult): EvalResult {
356+
private fun <T> withEvalContext(action: () -> T): T {
351357
return synchronized(this) {
352358
evalContextEnabled = true
353359
try {
@@ -365,14 +371,14 @@ class ReplForJupyterImpl(
365371
else context.compilationConfiguration.asSuccess()
366372
}
367373

368-
/**
369-
* Used for debug purposes.
370-
* @see ReplCommand
371-
*/
374+
@TestOnly
375+
@Suppress("unused")
372376
private fun printVariables(isHtmlFormat: Boolean = false) = log.debug(
373377
if (isHtmlFormat) notebook.variablesReportAsHTML() else notebook.variablesReport()
374378
)
375379

380+
@TestOnly
381+
@Suppress("unused")
376382
private fun printUsagesInfo(cellId: Int, usedVariables: Set<String>?) {
377383
log.debug(buildString {
378384
if (usedVariables == null || usedVariables.isEmpty()) {
@@ -386,7 +392,7 @@ class ReplForJupyterImpl(
386392
})
387393
}
388394

389-
fun evalEx(code: Code, displayHandler: DisplayHandler?, jupyterId: Int): EvalResult {
395+
fun evalEx(code: Code, displayHandler: DisplayHandler?, jupyterId: Int): EvalResultEx {
390396
return withEvalContext {
391397
rethrowAsLibraryException(LibraryProblemPart.BEFORE_CELL_CALLBACKS) {
392398
beforeCellExecution.forEach { executor.execute(it) }
@@ -426,8 +432,15 @@ class ReplForJupyterImpl(
426432
// printUsagesInfo(jupyterId, cellVariables[jupyterId - 1])
427433
val serializedData = variablesSerializer.serializeVariables(jupyterId - 1, notebook.variablesState, notebook.unchangedVariables())
428434

429-
EvalResult(
435+
GlobalScope.launch(Dispatchers.Default) {
436+
variablesSerializer.tryValidateCache(jupyterId - 1, notebook.cellVariables)
437+
}
438+
439+
EvalResultEx(
430440
result.result.value,
441+
rendered,
442+
result.scriptInstance,
443+
result.result.name,
431444
EvaluatedSnippetMetadata(newClasspath, compiledData, newImports, serializedData),
432445
)
433446
}
@@ -525,8 +538,9 @@ class ReplForJupyterImpl(
525538
doWithLock(SerializationArgs(descriptorsState, cellId = cellId, callback = callback), serializationQueue, SerializationReply(cellId, descriptorsState), ::doSerializeVariables)
526539
}
527540

528-
override suspend fun serializeVariables(topLevelVarName: String, descriptorsState: Map<String, SerializedVariablesState>, callback: (SerializationReply) -> Unit) {
529-
doWithLock(SerializationArgs(descriptorsState, topLevelVarName = topLevelVarName, callback = callback), serializationQueue, SerializationReply(), ::doSerializeVariables)
541+
override suspend fun serializeVariables(topLevelVarName: String, descriptorsState: Map<String, SerializedVariablesState>, pathToDescriptor: List<String>,
542+
callback: (SerializationReply) -> Unit) {
543+
doWithLock(SerializationArgs(descriptorsState, topLevelVarName = topLevelVarName, callback = callback, pathToDescriptor = pathToDescriptor), serializationQueue, SerializationReply(), ::doSerializeVariables)
530544
}
531545

532546
private fun doSerializeVariables(args: SerializationArgs): SerializationReply {
@@ -537,7 +551,7 @@ class ReplForJupyterImpl(
537551
finalAns
538552
}
539553
args.descriptorsState.forEach { (name, state) ->
540-
resultMap[name] = variablesSerializer.doIncrementalSerialization(cellId - 1, name, state)
554+
resultMap[name] = variablesSerializer.doIncrementalSerialization(cellId - 1, name, state, args.pathToDescriptor)
541555
}
542556
log.debug("Serialization cellID: $cellId")
543557
log.debug("Serialization answer: ${resultMap.entries.first().value.fieldDescriptor}")
@@ -581,6 +595,7 @@ class ReplForJupyterImpl(
581595
val descriptorsState: Map<String, SerializedVariablesState>,
582596
var cellId: Int = -1,
583597
val topLevelVarName: String = "",
598+
val pathToDescriptor: List<String> = emptyList(),
584599
override val callback: (SerializationReply) -> Unit
585600
) : LockQueueArgs<SerializationReply>
586601

Diff for: src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl/impl/InternalEvaluatorImpl.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ internal class InternalEvaluatorImpl(
151151

152152
private fun updateVariablesState(cellId: Int) {
153153
variablesWatcher.removeOldUsages(cellId)
154-
155154
variablesHolder.forEach {
156155
val state = it.value as VariableStateImpl
157156
val oldValue = state.stringValue
@@ -173,11 +172,23 @@ internal class InternalEvaluatorImpl(
173172
it.name
174173
}.toHashSet()
175174
val ans = mutableMapOf<String, VariableStateImpl>()
175+
// maybe remove known declarations
176+
val addedDeclarations = mutableSetOf<String>()
177+
176178
fields.forEach { property ->
177179
if (!memberKPropertiesNames.contains(property.name)) return@forEach
178180

179181
val state = VariableStateImpl(property, cellClassInstance)
180182
variablesWatcher.addDeclaration(cellId, property.name)
183+
addedDeclarations.add(property.name)
184+
185+
// try check values
186+
if (variablesHolder.containsKey(property.name)) {
187+
val seenState = variablesHolder[property.name]
188+
if (seenState?.value?.equals(state.value) == true) {
189+
addedDeclarations.remove(property.name)
190+
}
191+
}
181192

182193
// it was val, now it's var
183194
if (isValField(property)) {
@@ -189,6 +200,9 @@ internal class InternalEvaluatorImpl(
189200

190201
ans[property.name] = state
191202
}
203+
// remove old
204+
variablesWatcher.removeOldDeclarations(cellId, addedDeclarations)
205+
192206
return ans
193207
}
194208

@@ -199,7 +213,7 @@ internal class InternalEvaluatorImpl(
199213
private fun updateDataAfterExecution(lastExecutionCellId: Int, resultValue: ResultValue) {
200214
variablesWatcher.ensureStorageCreation(lastExecutionCellId)
201215
variablesHolder += getVisibleVariables(resultValue, lastExecutionCellId)
202-
216+
// remove unreached variables
203217
updateVariablesState(lastExecutionCellId)
204218
}
205219
}

0 commit comments

Comments
 (0)