From 094ac8cb488bcc460ceb3d296ade7fdbb8926bc6 Mon Sep 17 00:00:00 2001 From: Fingercomp Date: Mon, 11 Aug 2025 23:48:53 +0300 Subject: [PATCH] Fix brain event snapshotting; remove obsolete synchronization --- .../scala/ocelot/desktop/ui/UiHandler.scala | 21 ++++++++++--------- .../ocelot/desktop/ui/widget/LogWidget.scala | 13 ++++++------ .../util/OcelotInterfaceLogStorage.scala | 16 +++++++------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/main/scala/ocelot/desktop/ui/UiHandler.scala b/src/main/scala/ocelot/desktop/ui/UiHandler.scala index 391838b..31a3918 100644 --- a/src/main/scala/ocelot/desktop/ui/UiHandler.scala +++ b/src/main/scala/ocelot/desktop/ui/UiHandler.scala @@ -26,6 +26,7 @@ import javax.imageio.ImageIO import javax.swing.JFileChooser import scala.annotation.tailrec import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer import scala.concurrent.duration.DurationInt import scala.util.Try @@ -437,24 +438,24 @@ object UiHandler extends Logging { } private def dispatchBrainEvents(): Unit = { + import totoro.ocelot.brain + val cutoff = BrainEvents.events.peekLast() if (cutoff == null) { return } - // we're effectively taking a snapshot of all events we have received at the point of the call. + // we're taking a snapshot of all events we have received at the point of the call. // new events may still be added to the queue, but we'll defer everything after the cutoff until the next update. - val events = LazyList.continually(BrainEvents.events.poll()).takeWhile({ - var inSnapshot = true + var cutoffReached = false + val events = ArrayBuffer.empty[brain.event.Event] - { event => - val pass = inSnapshot - inSnapshot = !(event eq cutoff) - - pass - } - }) + while (!cutoffReached) { + val event = BrainEvents.events.poll() + cutoffReached = event eq cutoff + events += event + } for (event <- events) { root.workspaceView.dispatchBrainEvent(event) diff --git a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala index e3ff90a..30f8126 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala @@ -9,7 +9,7 @@ import ocelot.desktop.ui.widget.LogWidget.{BorderThickness, EntryMargin, EntryPa import ocelot.desktop.util.{DrawUtils, Orientation} import scala.annotation.tailrec -import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable abstract class LogWidget extends Widget { override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) @@ -17,18 +17,17 @@ abstract class LogWidget extends Widget { private object MessageListWidget extends Widget { messageList => override protected val layout: Layout = new Layout(this) - // NOTE: access to entries must be synchronized! - private val entries = ArrayBuffer.empty[Entry] + private val entries = mutable.ArrayDeque.empty[Entry] val dummyEntry: Entry = new RxEntry("", nextMessageY) override def minimumSize: Size2D = Size2D(dummyEntry.minimumSize.width + 2 * EntryMargin, nextMessageY) - private def nextMessageY: Float = entries.synchronized { + private def nextMessageY: Float = { entries.lastOption.map(_.maxY).getOrElse(0f) + EntryMargin } - def addEntry(entry: LogEntry): Unit = entries.synchronized { + def addEntry(entry: LogEntry): Unit = { entries += (entry match { case LogEntry.Rx(message) => new RxEntry(message, nextMessageY) case LogEntry.Tx(message) => new TxEntry(message, nextMessageY) @@ -37,7 +36,7 @@ abstract class LogWidget extends Widget { parent.get.recalculateBoundsAndRelayout() } - def removeFirst(count: Int): Unit = entries.synchronized { + def removeFirst(count: Int): Unit = { if (count >= entries.length) { entries.clear() } else { @@ -68,7 +67,7 @@ abstract class LogWidget extends Widget { } } - override def draw(g: Graphics): Unit = entries.synchronized { + override def draw(g: Graphics): Unit = { val firstVisibleIdx = firstVisibleIdxSearch(parent.get.asInstanceOf[ScrollView].offset.y) for (entry <- entries.iterator.drop(firstVisibleIdx).takeWhile(_.absoluteBounds.y <= clippedBounds.max.y)) { diff --git a/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala b/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala index 8d8405b..820e81b 100644 --- a/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala +++ b/src/main/scala/ocelot/desktop/util/OcelotInterfaceLogStorage.scala @@ -22,8 +22,6 @@ trait OcelotInterfaceLogStorage extends EventAware with Persistable with Windowe private var _messageLimit: Int = 1000 - // NOTE: access must be synchronized! - // ocelot.log() is a direct method, so it may push events even if the tick lock is not acquired private val _entries = mutable.ArrayDeque.empty[LogEntry] eventHandlers += { @@ -98,7 +96,7 @@ trait OcelotInterfaceLogStorage extends EventAware with Persistable with Windowe addEntries(entries.toSeq) } - override def save(nbt: NBTTagCompound): Unit = _entries.synchronized { + override def save(nbt: NBTTagCompound): Unit = { super.save(nbt) nbt.setTagList(EntriesTag, _entries.map(saveEntry(_).asInstanceOf[NBTBase]).asJava) @@ -107,36 +105,36 @@ trait OcelotInterfaceLogStorage extends EventAware with Persistable with Windowe def messageLimit: Int = _messageLimit - def messageLimit_=(limit: Int): Unit = _entries.synchronized { + def messageLimit_=(limit: Int): Unit = { require(limit > 0) ensureFreeSpace(_entries.length - limit) _messageLimit = limit } - def entryCount: Int = _entries.synchronized { + def entryCount: Int = { _entries.length } - def clear(): Unit = _entries.synchronized { + def clear(): Unit = { val count = _entries.length _entries.clear() window.onMessagesRemoved(count) } - private def addEntry(entry: LogEntry): Unit = _entries.synchronized { + private def addEntry(entry: LogEntry): Unit = { ensureFreeSpace(1) _entries += entry onMessagesAdded(Some(entry)) } - private def addEntries(entries: Seq[LogEntry]): Unit = _entries.synchronized { + private def addEntries(entries: Seq[LogEntry]): Unit = { ensureFreeSpace(entries.length) val prevCount = _entries.length _entries ++= entries.view.takeRight(messageLimit) onMessagesAdded(_entries.view.takeRight(_entries.length - prevCount)) } - private def ensureFreeSpace(n: Int): Unit = _entries.synchronized { + private def ensureFreeSpace(n: Int): Unit = { val prevCount = _entries.length _entries.takeRightInPlace(messageLimit - n) val removedCount = prevCount - _entries.length