X-Git-Url: https://mograsim.net/gitweb/?a=blobdiff_plain;f=plugins%2Fnet.mograsim.plugin.core%2Fsrc%2Fnet%2Fmograsim%2Fplugin%2Flaunch%2FMainMemoryBlockExtension.java;h=16c6db0a394c8f2f5b32982df8b50c9c35883dc9;hb=648fc6e69e09fe4467cb6bac47934be1a7dcf0d6;hp=ff633fe1998034c14b51f6f4a710d0dbd9eb5f3b;hpb=435134a2171ff1092eb87cbd1abe5fd3935118cc;p=Mograsim.git diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java index ff633fe1..16c6db0a 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java @@ -1,5 +1,7 @@ package net.mograsim.plugin.launch; +import static net.mograsim.plugin.preferences.PluginPreferences.MAX_MEMORY_CHANGE_INTERVAL; + import java.math.BigInteger; import java.util.HashSet; import java.util.Set; @@ -24,7 +26,8 @@ import net.mograsim.plugin.MograsimActivator; public class MainMemoryBlockExtension extends PlatformObject implements IMemoryBlockExtension { - // TODO do we want to make the memory accessible byte-wise? + private static final byte MEM_BYTE_FLAGS = (byte) (MemoryByte.READABLE | MemoryByte.WRITABLE | MemoryByte.ENDIANESS_KNOWN + | MemoryByte.BIG_ENDIAN); private final String expression; private final MachineDebugTarget debugTarget; @@ -44,6 +47,12 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB private final MemoryCellModifiedListener memListener; private final AtomicBoolean memListenerRegistered; + private final int maxContentChangeInterval; + private final Object contentChangeLock; + private Thread contentChangeThread; + private long nextContentChangeAllowedMillis; + private boolean contentChangeQueued; + public MainMemoryBlockExtension(MachineDebugTarget debugTarget, String expression, @SuppressWarnings("unused") Object expressionContext) throws DebugException { @@ -64,13 +73,18 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB if (baseAddrWords.compareTo(minAddrWords) < 0 || baseAddrWords.compareTo(maxAddrWords) > 0) throwDebugException("Base address out of range"); - if (baseAddrWords.add(lengthWords).compareTo(maxAddrWords) > 0) + if (baseAddrWords.add(lengthWords).subtract(BigInteger.ONE).compareTo(maxAddrWords) > 0) throwDebugException("End address out of range"); this.clients = new HashSet<>(); // don't check whether the address is in range, because this memory block could be read outside its "range" - this.memListener = a -> fireContentChangeEvent(); + this.memListener = a -> queueFireContentChangeEvent(); this.memListenerRegistered = new AtomicBoolean(); + + // TODO add a listener + this.maxContentChangeInterval = MograsimActivator.instance().getPluginPrefs().getInt(MAX_MEMORY_CHANGE_INTERVAL); + this.contentChangeLock = new Object(); + this.nextContentChangeAllowedMillis = System.currentTimeMillis() - maxContentChangeInterval - 1; } @Override @@ -183,7 +197,7 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB @Override public int getAddressSize() throws DebugException { - return Long.BYTES; + return (getBigLength().bitLength() + 7) / 8; } @Override @@ -231,9 +245,9 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB int l = wordBytes[0] == 0 ? 1 : 0; int k; for (k = 0; k < cellWidthBytes - wordBytes.length + l; k++) - bytes[i + k] = new MemoryByte(); + bytes[i + k] = new MemoryByte((byte) 0, MEM_BYTE_FLAGS); for (; k < cellWidthBytes; k++, l++) - bytes[i + k] = new MemoryByte(wordBytes[l]); + bytes[i + k] = new MemoryByte(wordBytes[l], MEM_BYTE_FLAGS); } else for (int k = 0; k < cellWidthBytes; k++) bytes[i + k] = new MemoryByte((byte) 0, (byte) 0); @@ -265,7 +279,7 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB if (!clients.isEmpty()) registerMemoryListener(); - fireContentChangeEvent(); + queueFireContentChangeEvent(); } @Override @@ -302,6 +316,24 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB { if (memListenerRegistered.getAndSet(false)) mem.deregisterCellModifiedListener(memListener); + + Thread contentChangeThreadLocal; + synchronized (contentChangeLock) + { + contentChangeThreadLocal = contentChangeThread; + // set contentChangeQueued here to prevent the following scenario: + // 1. A change event is requested -> it gets fired "directly" + // 2. A second change event is requested during the "cooldown time" -> a queue thread gets started + // 3. The last client is disconnected -> the queue thread is interrupted + // 4. A new client is connected + // 5. A third change event is requested; queueFireContentChangeEvent locks contentChangeLock + // before the queue thread locks contentChangeLock. + // Now queueFireContentChangeEvent would return doing nothing, since contentChangeQueued still is true, + // causing a change event to be missed. + contentChangeQueued = false; + } + if (contentChangeThreadLocal != null) + contentChangeThreadLocal.interrupt(); } @Override @@ -323,10 +355,62 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB return cellWidthBytes; } + private void queueFireContentChangeEvent() + { + long sleepTime; + boolean fireInOwnThread = false; + synchronized (contentChangeLock) + { + if (contentChangeQueued) + return; + long nextContentChangeAllowedMillisLocal = nextContentChangeAllowedMillis; + long now = System.currentTimeMillis(); + sleepTime = nextContentChangeAllowedMillisLocal - now; + if (sleepTime >= 0) + { + fireInOwnThread = true; + contentChangeQueued = true; + nextContentChangeAllowedMillis = nextContentChangeAllowedMillisLocal + maxContentChangeInterval; + } else + { + fireInOwnThread = false; + nextContentChangeAllowedMillis = now + maxContentChangeInterval; + } + } + if (fireInOwnThread) + { + // the following two statements can't cause racing problems since we set contentChangeQueued to true, + // which means no-one will write this field until the thread started: + // this method will never get here, and in this moment there is no (other) content change thread running, + // since contentChangeQueued was false + contentChangeThread = new Thread(() -> + { + boolean interrupted = false; + try + { + Thread.sleep(sleepTime); + } + catch (@SuppressWarnings("unused") InterruptedException e) + { + interrupted = true; + } + synchronized (contentChangeLock) + { + contentChangeThread = null; + contentChangeQueued = false; + } + if (!interrupted && !Thread.interrupted()) + fireContentChangeEventNow(); + }); + contentChangeThread.start(); + } else + fireContentChangeEventNow(); + } + /** * Fires a terminate event for this debug element. */ - private void fireContentChangeEvent() + private void fireContentChangeEventNow() { fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT)); }