[BAEL-7044] Shared Memory Between Two JVMs (#15162)

* [BAEL-4849] Article code

* [BAEL-4968] Article code

* [BAEL-4968] Article code

* [BAEL-4968] Article code

* [BAEL-4968] Remove extra comments

* [BAEL-5258] Article Code

* [BAEL-2765] PKCE Support for Secret Clients

* [BAEL-5698] Article code

* [BAEL-5698] Article code

* [BAEL-5905] Initial code

* [BAEL-5905] Article code

* [BAEL-5905] Relocate article code to new module

* [BAEL-6275] PostgreSQL NOTIFY/LISTEN

* [BAEL-6275] Minor correction

* BAEL-6138

* [BAEL-6138] WIP - LiveTest

* [BAEL-6138] Tutorial Code

* [BAEL-6138] Tutorial Code

* [BAEL-6694] Article Code

* [BAEL-7044] Tutorial code

---------

Co-authored-by: Philippe Sevestre <psevestre@gmail.com>
This commit is contained in:
psevestre 2023-11-09 18:56:11 -03:00 committed by GitHub
parent 499ed4bbbd
commit 0b0db24ebb
7 changed files with 480 additions and 0 deletions

View File

@ -0,0 +1,8 @@
@rem This bat file starts the consumer and producer (more or less) at the same time
cd target\classes
rem start java --add-opens java.base/java.nio=ALL-UNNAMED com.baeldung.sharedmem.ProducerApp c:\lixo\sharedmem.bin 65536
rem start java --add-opens java.base/java.nio=ALL-UNNAMED com.baeldung.sharedmem.ConsumerApp c:\lixo\sharedmem.bin 65536
start %JAVA_HOME%\bin\java com.baeldung.sharedmem.ProducerApp c:\lixo\sharedmem.bin 65536
start %JAVA_HOME%\bin\java com.baeldung.sharedmem.ConsumerApp c:\lixo\sharedmem.bin 65536
cd ..
cd ..

View File

@ -0,0 +1,10 @@
@rem This bat file starts the consumer and producer (more or less) at the same time
del target\sharedmem.bin
echo "" > target\sharedmem.bin
cd target\classes
rem start java --add-opens java.base/java.nio=ALL-UNNAMED com.baeldung.sharedmem.ProducerAppWithSpinLock ..\sharedmem.bin 65536
rem start java --add-opens java.base/java.nio=ALL-UNNAMED com.baeldung.sharedmem.ConsumerAppWithSpinLock ..\sharedmem.bin 65536
start %JAVA_HOME%\bin\java com.baeldung.sharedmem.ProducerAppWithSpinLock ..\sharedmem.bin 65536
start %JAVA_HOME%\bin\java com.baeldung.sharedmem.ConsumerAppWithSpinLock ..\sharedmem.bin 65536
cd ..
cd ..

View File

@ -0,0 +1,94 @@
package com.baeldung.sharedmem;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Random;
public class ConsumerApp {
public static void main(String[] args) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.digest(new byte[256]);
byte[] dummy = digest.digest();
int hashLen = dummy.length;
System.out.println("Starting consumer iterations...");
long size = Long.parseLong(args[1]);
MappedByteBuffer shm = createSharedMemory(args[0], size + hashLen);
long start = System.currentTimeMillis();
long iterations = 0;
int capacity = shm.capacity();
long matchCount = 0;
long mismatchCount = 0;
byte[] expectedHash = new byte[hashLen];
while (System.currentTimeMillis() - start < 30_000) {
for (int i = 0; i < capacity - hashLen; i++) {
byte value = shm.get(i);
digest.update(value);
}
byte[] hash = digest.digest();
shm.position(capacity-hashLen);
shm.get(expectedHash);
if (Arrays.equals(hash, expectedHash)) {
matchCount++;
} else {
mismatchCount++;
}
iterations++;
}
System.out.printf("%d iterations run. matches=%d, mismatches=%d\n", iterations, matchCount, mismatchCount);
System.out.println("Press <enter> to exit");
System.console()
.readLine();
}
private static MappedByteBuffer createSharedMemory(String path, long size) {
try (FileChannel fc = (FileChannel) Files.newByteChannel(
new File(path).toPath(),
EnumSet.of(
StandardOpenOption.CREATE,
StandardOpenOption.SPARSE,
StandardOpenOption.WRITE,
StandardOpenOption.READ))) {
return fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private static long getBufferAddress(MappedByteBuffer shm) {
try {
Class<?> cls = shm.getClass();
Method maddr = cls.getMethod("address");
maddr.setAccessible(true);
Long addr = (Long) maddr.invoke(shm);
if (addr == null) {
throw new RuntimeException("Unable to retrieve buffer's address");
}
return addr;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -0,0 +1,119 @@
package com.baeldung.sharedmem;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Random;
public class ConsumerAppWithSpinLock {
public static void main(String[] args) {
try {
// Small wait to ensure the Producer gets the first round. Otherwise the hash will be invalid
Thread.sleep(1000);
run(args);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
System.console()
.printf("Press <enter> to continue");
System.console()
.readLine();
}
}
private static void run(String args[]) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.digest(new byte[256]);
byte[] dummy = digest.digest();
int hashLen = dummy.length;
long size = Long.parseLong(args[1]);
MappedByteBuffer shm = createSharedMemory(args[0], size + hashLen);
long addr = getBufferAddress(shm);
System.out.printf("Buffer address: 0x%08x\n", addr);
Random rnd = new Random();
long start = System.currentTimeMillis();
long iterations = 0;
int capacity = shm.capacity();
System.out.println("Starting consumer iterations...");
long matchCount = 0;
long mismatchCount = 0;
byte[] expectedHash = new byte[hashLen];
SpinLock lock = new SpinLock(addr);
while (System.currentTimeMillis() - start < 30_000) {
if (!lock.tryLock(5_000)) {
throw new RuntimeException("Unable to acquire lock");
}
try {
for (int i = 4; i < capacity - hashLen; i++) {
byte value = shm.get(i);
digest.update(value);
}
byte[] hash = digest.digest();
shm.position(capacity-hashLen);
shm.get(expectedHash);
if (Arrays.equals(hash, expectedHash)) {
matchCount++;
} else {
mismatchCount++;
}
iterations++;
} finally {
lock.unlock();
}
}
System.out.printf("%d iteractions run. matches=%d, mismatches=%d\n", iterations, matchCount, mismatchCount);
}
private static MappedByteBuffer createSharedMemory(String path, long size) {
try (FileChannel fc = (FileChannel) Files.newByteChannel(
new File(path).toPath(),
EnumSet.of(
StandardOpenOption.CREATE,
StandardOpenOption.SPARSE,
StandardOpenOption.WRITE,
StandardOpenOption.READ))) {
return fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private static long getBufferAddress(MappedByteBuffer shm) {
try {
Class<?> cls = shm.getClass();
Method maddr = cls.getMethod("address");
maddr.setAccessible(true);
Long addr = (Long) maddr.invoke(shm);
if (addr == null) {
throw new RuntimeException("Unable to retrieve buffer's address");
}
return addr;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -0,0 +1,89 @@
package com.baeldung.sharedmem;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.Random;
public class ProducerApp {
public static void main(String[] args) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.digest(new byte[256]);
byte[] dummy = digest.digest();
int hashLen = dummy.length;
long size = Long.parseLong(args[1]);
MappedByteBuffer shm = createSharedMemory(args[0], size + hashLen);
System.out.println("Starting producer iterations...");
long start = System.currentTimeMillis();
long iterations = 0;
int capacity = shm.capacity();
Random rnd = new Random();
while(System.currentTimeMillis() - start < 30000) {
for (int i = 0; i < capacity - hashLen; i++) {
byte value = (byte) (rnd.nextInt(256) & 0x00ff);
digest.update(value);
shm.put(i, value);
}
// Write hash at the end
byte[] hash = digest.digest();
shm.position(capacity - hashLen);
shm.put(hash);
iterations++;
}
System.out.printf("%d iterations run\n", iterations);
System.out.println("Press <enter> to exit");
System.console().readLine();
}
private static long getBufferAddress(MappedByteBuffer shm) {
try {
Class<?> cls = shm.getClass();
Method maddr = cls.getMethod("address");
maddr.setAccessible(true);
Long addr = (Long) maddr.invoke(shm);
if ( addr == null ) {
throw new RuntimeException("Unable to retrieve buffer's address");
}
return addr;
}
catch( NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private static MappedByteBuffer createSharedMemory(String path, long size) {
try (FileChannel fc = (FileChannel)Files.newByteChannel(new File(path).toPath(),
EnumSet.of(
StandardOpenOption.CREATE,
StandardOpenOption.SPARSE,
StandardOpenOption.WRITE,
StandardOpenOption.READ))) {
return fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
}
catch( IOException ioe) {
throw new RuntimeException(ioe);
}
}
}

View File

@ -0,0 +1,117 @@
package com.baeldung.sharedmem;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.Random;
public class ProducerAppWithSpinLock {
public static void main(String[] args) {
try {
run(args);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
System.console()
.printf("Press <enter> to continue");
System.console()
.readLine();
}
}
public static void run(String[] args) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.digest(new byte[256]);
byte[] dummy = digest.digest();
int hashLen = dummy.length;
long size = Long.parseLong(args[1]);
MappedByteBuffer shm = createSharedMemory(args[0], size + hashLen);
// Cleanup lock area
shm.putInt(0,0);
long addr = getBufferAddress(shm);
System.out.printf("Buffer address: 0x%08x\n",addr);
Random rnd = new Random();
long start = System.currentTimeMillis();
long iterations = 0;
int capacity = shm.capacity();
System.out.println("Starting producer iterations...");
SpinLock lock = new SpinLock(addr);
while(System.currentTimeMillis() - start < 30000) {
if(!lock.tryLock(5000)) {
throw new RuntimeException("Unable to acquire lock");
}
try {
// Skip the first 4 bytes, as they're used by the lock
for (int i = 4; i < capacity - hashLen; i++) {
byte value = (byte) (rnd.nextInt(256) & 0x00ff);
digest.update(value);
shm.put(i, value);
}
// Write hash at the end
byte[] hash = digest.digest();
shm.position(capacity-hashLen);
shm.put(hash);
iterations++;
}
finally {
lock.unlock();
}
}
System.out.printf("%d iterations run\n", iterations);
}
private static long getBufferAddress(MappedByteBuffer shm) {
try {
Class<?> cls = shm.getClass();
Method maddr = cls.getMethod("address");
maddr.setAccessible(true);
Long addr = (Long) maddr.invoke(shm);
if ( addr == null ) {
throw new RuntimeException("Unable to retrieve buffer's address");
}
return addr;
}
catch( NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private static MappedByteBuffer createSharedMemory(String path, long size) {
try (FileChannel fc = (FileChannel)Files.newByteChannel(new File(path).toPath(),
EnumSet.of(
StandardOpenOption.CREATE,
StandardOpenOption.SPARSE,
StandardOpenOption.WRITE,
StandardOpenOption.READ))) {
return fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
}
catch( IOException ioe) {
throw new RuntimeException(ioe);
}
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.sharedmem;
//import sun.misc.Unsafe;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class SpinLock {
private static final Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
}
catch(NoSuchFieldException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private final long addr;
public SpinLock(long addr) {
this.addr = addr;
}
public boolean tryLock(long maxWait) {
long deadline = System.currentTimeMillis() + maxWait;
while(System.currentTimeMillis() < deadline ) {
if ( unsafe.compareAndSwapInt(null,addr,0,1)) {
return true;
}
}
return false;
}
public void unlock() {
unsafe.putInt(addr,0);
}
}