/*-
*******************************************************************************
* Copyright (c) 2011, 2016 Diamond Light Source Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Gerring - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.dawnsci.hdf5.swmr;
import java.io.File;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import hdf.hdf5lib.H5;
import hdf.hdf5lib.HDF5Constants;
import hdf.hdf5lib.exceptions.HDF5Exception;
import hdf.hdf5lib.exceptions.HDF5LibraryException;
public class TestSWMR {
private final static String filename = "test-scratch/testSWMR.h5";
@Before
public void setup() throws NullPointerException, HDF5Exception {
// create skeleton file with 1D dataset of unlimited length
File parent = new File(filename).getParentFile();
if (!parent.exists()) {
parent.mkdir();
}
long fapl = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
H5.H5Pset_libver_bounds(fapl, HDF5Constants.H5F_LIBVER_LATEST, HDF5Constants.H5F_LIBVER_LATEST);
long fileID = H5.H5Fcreate(filename, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, fapl);
long dsID = H5.H5Screate_simple(1, new long[] {0}, new long[] {HDF5Constants.H5S_UNLIMITED});
long dcpl = H5.H5Pcreate(HDF5Constants.H5P_DATASET_CREATE);
H5.H5Pset_chunk(dcpl, 1, new long[] {2});
long dID = H5.H5Dcreate(fileID, "/data",
HDF5Constants.H5T_IEEE_F32LE, dsID,
HDF5Constants.H5P_DEFAULT, dcpl, HDF5Constants.H5P_DEFAULT);
H5.H5Dflush(dID);
H5.H5Drefresh(dID);
H5.H5Dclose(dID);
H5.H5Pclose(dcpl);
H5.H5Sclose(dsID);
H5.H5Fclose(fileID);
H5.H5Pclose(fapl);
}
/**
* open file in SWMR or non-SWMR, read dataset and wait
* @param file
* @param swmr
* @param sleep
* @throws HDF5LibraryException
*/
private void readSWMR(String file, boolean swmr, long sleep) throws HDF5LibraryException {
long fapl = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
int access = HDF5Constants.H5F_ACC_RDONLY;
if (swmr) {
H5.H5Pset_libver_bounds(fapl, HDF5Constants.H5F_LIBVER_LATEST, HDF5Constants.H5F_LIBVER_LATEST);
access |= HDF5Constants.H5F_ACC_SWMR_READ;
}
long fileID = H5.H5Fopen(file, access, fapl);
long dID = H5.H5Dopen(fileID, "/data", HDF5Constants.H5P_DEFAULT);
long fsID = H5.H5Dget_space(dID);
int rank = (int) H5.H5Sget_simple_extent_ndims(fsID);
long[] shape = new long[rank];
long[] maxShape = new long[rank];
H5.H5Sget_simple_extent_dims(fsID, shape, maxShape);
try {
Assert.assertEquals(1, rank);
Assert.assertArrayEquals(new long[] {4}, shape);
Assert.assertArrayEquals(new long[] {HDF5Constants.H5S_UNLIMITED}, maxShape);
float[] buf = new float[4];
H5.H5Dread_float(dID, HDF5Constants.H5T_IEEE_F32LE, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, buf);
Assert.assertArrayEquals(new float[] {0, 1, 2, 3}, buf, 1f-5);
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
} finally {
H5.H5Sclose(fsID);
H5.H5Dclose(dID);
H5.H5Fclose(fileID);
H5.H5Pclose(fapl);
}
}
@Test
public void testReadSWMR() throws Exception {
readSWMR("testfiles/testnonSWMR.h5", true, 0);
readSWMR("testfiles/testSWMR.h5", true, 0);
}
@Test
public void testRead() throws Exception {
readSWMR("testfiles/testnonSWMR.h5", false, 0);
readSWMR("testfiles/testSWMR.h5", false, 0);
}
private Thread createReadThread(final String file, final boolean swmr, final long sleep) {
return new Thread(new Runnable() {
@Override
public void run() {
try {
readSWMR(file, swmr, sleep);
} catch (Exception e) {
System.err.println(Thread.currentThread().getName());
// e.printStackTrace();
}
}
}, "Thd read: swmr=" + swmr + ", sleep=" + sleep + "ms");
}
private static final long SLEEP = 500;
@Test
public void testReadClosedSimultaneously() throws HDF5Exception, NullPointerException {
long sleep = SLEEP;
String[] files = new String[] {"testfiles/testnonSWMR.h5", "testfiles/testSWMR.h5"};
for (int k = 0; k < 2; k++) {
String file = files[k];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
System.err.println(file + " " + i + " " + j);
Thread ta = createReadThread(file, i != 0, sleep);
Thread tb = createReadThread(file, j != 0, sleep);
System.err.println("Starting A");
ta.start();
try {
Thread.sleep(sleep / 3);
} catch (InterruptedException e) {
}
System.err.println("Starting B");
tb.start();
while (ta.isAlive() || tb.isAlive()) {
try {
Thread.sleep(sleep / 2);
} catch (InterruptedException e) {
}
}
System.err.println("A & B finished");
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
}
}
}
}
/**
* open and write in SWMR mode and wait then optionally extend, write and wait again
* @param file
* @param sleep
* @param second
* @throws HDF5LibraryException
*/
private void writeSWMR(String file, long sleep, boolean second) throws HDF5LibraryException {
long fapl = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
H5.H5Pset_libver_bounds(fapl, HDF5Constants.H5F_LIBVER_LATEST, HDF5Constants.H5F_LIBVER_LATEST);
long fileID = H5.H5Fopen(file, HDF5Constants.H5F_ACC_RDWR, fapl);
float[] data = new float[] {0, 1, 2, 3};
H5.H5Fstart_swmr_write(fileID);
long dID = H5.H5Dopen(fileID, "/data", HDF5Constants.H5P_DEFAULT);
H5.H5Dset_extent(dID, new long[] {4});
H5.H5Dwrite_float(dID, HDF5Constants.H5T_IEEE_F32LE, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, data);
H5.H5Dflush(dID);
H5.H5Drefresh(dID);
float[] buf = new float[4];
H5.H5Dread_float(dID, HDF5Constants.H5T_IEEE_F32LE, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, buf);
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
if (second) {
data = new float[] {0, 1, 2, 3, 0, 1, 2, 3};
H5.H5Dset_extent(dID, new long[] {8});
H5.H5Dwrite_float(dID, HDF5Constants.H5T_IEEE_F32LE, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, data);
H5.H5Dflush(dID);
H5.H5Drefresh(dID);
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
}
H5.H5Dclose(dID);
H5.H5Fclose(fileID);
H5.H5Pclose(fapl);
}
@Test
public void testWrite() throws HDF5Exception, NullPointerException {
writeSWMR(filename, 0, false);
}
@Test
public void testMultipleWrite() throws HDF5Exception, NullPointerException {
writeSWMR(filename, 0, true);
}
private Thread createWriteThread(final String file, final long sleep, final boolean second) {
return new Thread(new Runnable() {
@Override
public void run() {
try {
writeSWMR(file, sleep, second);
} catch (HDF5LibraryException e) {
System.err.println(Thread.currentThread().getName());
// e.printStackTrace();
}
}
}, "Thd write: second=" + second + ", sleep=" + sleep + "ms");
}
@Test
public void testReadOpenSimultaneously() throws HDF5Exception, NullPointerException {
long sleep = SLEEP;
Thread tw = createWriteThread(filename, 12 * sleep, false);
tw.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
String file = filename;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
System.err.println(file + " " + i + " " + j);
Thread ta = createReadThread(file, i != 0, sleep);
Thread tb = createReadThread(file, j != 0, sleep);
System.err.println("Starting A");
ta.start();
try {
Thread.sleep(sleep / 3);
} catch (InterruptedException e) {
}
System.err.println("Starting B");
tb.start();
while (ta.isAlive() || tb.isAlive()) {
try {
Thread.sleep(sleep / 2);
} catch (InterruptedException e) {
}
}
System.err.println("A & B finished");
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
}
}
try {
System.err.println("Waiting to join...");
tw.join();
} catch (InterruptedException e) {
}
System.err.println("Finished");
}
@Test
public void testWriteReadSWMR() throws HDF5LibraryException {
long sleep = SLEEP;
Thread tw = createWriteThread(filename, sleep, false);
tw.start();
try {
Thread.sleep(sleep/3);
} catch (InterruptedException e) {
}
try {
readSWMR(filename, true, 0);
} finally {
try {
tw.join();
} catch (InterruptedException e) {
}
}
}
@Test
public void testWriteReadNonSWMR() throws HDF5LibraryException {
long sleep = SLEEP;
Thread tw = createWriteThread(filename, sleep, false);
tw.start();
try {
Thread.sleep(sleep/3);
} catch (InterruptedException e) {
}
try {
readSWMR(filename, false, 0);
} finally {
try {
tw.join();
} catch (InterruptedException e) {
}
}
}
@Test
public void testWriteReadEarlyNonSWMR() throws HDF5LibraryException {
long sleep = SLEEP;
Thread tw = createWriteThread(filename, sleep, false);
tw.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
try {
readSWMR(filename, false, 0);
Assert.fail("Should not failed as not written yet");
} catch (AssertionError e) {
// success
} finally {
try {
tw.join();
} catch (InterruptedException e) {
}
}
}
@Test
public void testWriteReadEarlySWMR() throws HDF5LibraryException {
long sleep = SLEEP;
Thread tw = createWriteThread(filename, sleep, false);
tw.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
try {
readSWMR(filename, true, 0);
Assert.fail("Should not failed as not written yet");
} catch (AssertionError e) {
// success
} finally {
try {
tw.join();
} catch (InterruptedException e) {
}
}
}
@Test
public void testMissingExtLink() throws HDF5LibraryException, NullPointerException {
long fileID = H5.H5Fcreate("test-scratch/missinglink.h5", HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
H5.H5Lcreate_external("missing.h5", "/target", fileID, "/ext", HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
H5.H5Fclose(fileID);
fileID = H5.H5Fopen("test-scratch/missinglink.h5", HDF5Constants.H5F_ACC_RDONLY, HDF5Constants.H5P_DEFAULT);
// H5L_info_t info = H5.H5Lget_info(fileID, "/ext", HDF5Constants.H5P_DEFAULT);
String[] linkName = new String[2]; // file name and file path
H5.H5Lget_value(fileID, "/ext", linkName, HDF5Constants.H5P_DEFAULT);
Assert.assertEquals("/target", linkName[0]);
Assert.assertEquals("missing.h5", linkName[1]);
}
}