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=b5d55c59d7069171bd928e4a945d9185ee4bc2b0;hp=9355ae2693e753e120982e9b3101f6dd595b6557;hpb=071016cea4c9942b7f5c26f4b7f0bbeb8ca37baf;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 9355ae26..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 @@ -228,11 +242,12 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB { BigInteger word = mem.getCellAsBigInteger(j.longValue()); byte[] wordBytes = word.toByteArray(); + int l = wordBytes[0] == 0 ? 1 : 0; int k; - for (k = 0; k < cellWidthBytes - wordBytes.length; k++) - bytes[i + k] = new MemoryByte(); - for (int l = 0; k < cellWidthBytes; k++) - bytes[i + k] = new MemoryByte(wordBytes[l]); + for (k = 0; k < cellWidthBytes - wordBytes.length + l; k++) + bytes[i + k] = new MemoryByte((byte) 0, MEM_BYTE_FLAGS); + for (; k < cellWidthBytes; k++, 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); @@ -264,7 +279,7 @@ public class MainMemoryBlockExtension extends PlatformObject implements IMemoryB if (!clients.isEmpty()) registerMemoryListener(); - fireContentChangeEvent(); + queueFireContentChangeEvent(); } @Override @@ -301,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 @@ -322,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)); }