| package org.apache.directmemory.memory; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Random; |
| |
| import junit.framework.Assert; |
| |
| import org.apache.directmemory.memory.MemoryManagerService; |
| import org.apache.directmemory.memory.MemoryTestUtils; |
| import org.apache.directmemory.memory.Pointer; |
| import org.junit.Test; |
| |
| public abstract class AbstractMemoryManagerServiceTest |
| { |
| |
| protected static final Random R = new Random(); |
| |
| protected static final int SMALL_PAYLOAD_LENGTH = 4; |
| protected static final byte[] SMALL_PAYLOAD = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| |
| |
| protected MemoryManagerService<Object> mms; |
| |
| protected abstract MemoryManagerService<Object> instanciateMemoryManagerService( int bufferSize ); |
| |
| |
| /** |
| * Test pointers allocation, when buffer size is not aligned with the size of stored objects. |
| * Null {@link Pointer} should be returned to allow {@link MemoryManagerService} to go to next step with allocation policy. |
| */ |
| @Test |
| public void testNotEnoughFreeSpace() |
| { |
| |
| // Storing a first payload of 4 bytes, 1 byte remaining in the buffer. When storing a second 4 bytes payload, an null pointer should be returned. |
| |
| final int BUFFER_SIZE = SMALL_PAYLOAD_LENGTH + 1; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| Pointer<Object> pointer1 = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointer1 ); |
| Assert.assertFalse( pointer1.isFree() ); |
| |
| Pointer<Object> pointer2 = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNull( pointer2 ); |
| |
| } |
| |
| |
| /** |
| * Ensure no byte is leaking when allocating several objects. |
| */ |
| @Test |
| public void testByteLeaking() |
| { |
| |
| // Initializing 1 buffer of 10*4 bytes, should be able to allocate 10 objects of 4 bytes. |
| |
| final int NUMBER_OF_OBJECTS = 10; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| Pointer<Object> pointer = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointer ); |
| } |
| |
| Pointer<Object> pointerNull = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNull( pointerNull ); |
| } |
| |
| /** |
| * Ensure memory usage is reported correctly |
| */ |
| @Test |
| public void testReportCorrectUsedMemory() |
| { |
| |
| // Initializing 1 buffer of 4*4 bytes, storing and freeing and storing again should report correct numbers. |
| |
| final int NUMBER_OF_OBJECTS = 4; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| Pointer<Object> lastPointer = null; |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| Pointer<Object> pointer = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointer ); |
| lastPointer = pointer; |
| } |
| |
| // Buffer is fully used. |
| Assert.assertEquals( BUFFER_SIZE, mms.used() ); |
| |
| Assert.assertNotNull( lastPointer ); |
| mms.free( lastPointer ); |
| |
| Pointer<Object> pointerNotNull = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointerNotNull ); |
| |
| // Buffer again fully used. |
| Assert.assertEquals( BUFFER_SIZE, mms.used() ); |
| |
| } |
| |
| /** |
| * Completely fill the buffer, free some pointer, reallocated the freed space, clear the buffer. The entire space should be |
| */ |
| @Test |
| public void testFullFillAndFreeAndClearBuffer() |
| { |
| |
| final int NUMBER_OF_OBJECTS = 10; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| Pointer<Object> pointerFull = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) ); |
| Assert.assertNotNull( pointerFull ); |
| mms.free( pointerFull ); |
| |
| final int size1 = R.nextInt( BUFFER_SIZE / 2 ) + 1; |
| Pointer<Object> pointer1 = mms.store( MemoryTestUtils.generateRandomPayload( size1 ) ); |
| Assert.assertNotNull( "Cannot store " + size1 + " bytes", pointer1 ); |
| |
| final int size2 = R.nextInt( ( BUFFER_SIZE - size1 ) / 2 ) + 1; |
| Pointer<Object> pointer2 = mms.store( MemoryTestUtils.generateRandomPayload( size2 ) ); |
| Assert.assertNotNull( "Cannot store " + size2 + " bytes", pointer2 ); |
| |
| final int size3 = R.nextInt( ( BUFFER_SIZE - size1 - size2 ) / 2 ) + 1; |
| Pointer<Object> pointer3 = mms.store( MemoryTestUtils.generateRandomPayload( size3 ) ); |
| Assert.assertNotNull( "Cannot store " + size3 + " bytes", pointer3 ); |
| |
| final int size4 = BUFFER_SIZE - size1 - size2 - size3; |
| Pointer<Object> pointer4 = mms.store( MemoryTestUtils.generateRandomPayload( size4 ) ); |
| Assert.assertNotNull( "Cannot store " + size4 + " bytes", pointer4 ); |
| |
| mms.free( pointer1 ); |
| Assert.assertTrue( pointer1.isFree() ); |
| |
| mms.free( pointer3 ); |
| |
| mms.free( pointer4 ); |
| |
| mms.free( pointer2 ); |
| |
| Assert.assertEquals( 0, mms.used() ); |
| |
| // As all pointers have been freeed, we should be able to reallocate the whole buffer |
| Pointer<Object> pointer6 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) ); |
| Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer6 ); |
| |
| mms.clear(); |
| |
| // As all pointers have been cleared, we should be able to reallocate the whole buffer |
| Pointer<Object> pointer7 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) ); |
| Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer7 ); |
| |
| mms.clear(); |
| |
| // As all pointers have been cleared, we should be able to reallocate the whole buffer |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| Pointer<Object> pointer = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointer ); |
| } |
| |
| mms.clear(); |
| |
| // As all pointers have been cleared, we should be able to reallocate the whole buffer |
| Pointer<Object> pointer8 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) ); |
| Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer8 ); |
| |
| mms.free( pointer8 ); |
| |
| // As all pointers have been cleared, we should be able to reallocate the whole buffer |
| for ( int i = 0; i < NUMBER_OF_OBJECTS * 10; i++ ) |
| { |
| Pointer<Object> pointer = mms.store( SMALL_PAYLOAD ); |
| Assert.assertNotNull( pointer ); |
| mms.free( pointer ); |
| } |
| |
| // After a clear occurs, pointers allocated before the clear should be set as "free" |
| Assert.assertTrue( pointer6.isFree() ); |
| Assert.assertTrue( pointer7.isFree() ); |
| |
| } |
| |
| @Test |
| public void testRandomPayload() |
| { |
| |
| final int NUMBER_OF_OBJECTS = 10; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| Pointer<Object> pointer = mms.store( payload ); |
| Assert.assertNotNull( pointer ); |
| byte[] fetchedPayload = mms.retrieve( pointer ); |
| Assert.assertEquals( new String( payload ), new String( fetchedPayload ) ); |
| if ( R.nextBoolean() ) |
| { |
| mms.free( pointer ); |
| } |
| } |
| |
| mms.clear(); |
| |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| Pointer<Object> pointer = mms.store( payload ); |
| Assert.assertNotNull( pointer ); |
| byte[] fetchedPayload = mms.retrieve( pointer ); |
| Assert.assertEquals( new String( payload ), new String( fetchedPayload ) ); |
| if ( R.nextBoolean() ) |
| { |
| mms.free( pointer ); |
| i--; |
| } |
| } |
| |
| } |
| |
| @Test |
| public void testStoreAllocAndFree() |
| { |
| |
| final int NUMBER_OF_OBJECTS = 100; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| List<Pointer<Object>> pointers = new ArrayList<Pointer<Object>>( NUMBER_OF_OBJECTS ); |
| for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ ) |
| { |
| byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| Pointer<Object> pointer = mms.store( payload ); |
| Assert.assertNotNull( pointer ); |
| pointers.add( pointer ); |
| byte[] fetchedPayload = mms.retrieve( pointer ); |
| Assert.assertEquals( new String( payload ), new String( fetchedPayload ) ); |
| } |
| |
| // Free 1/4 of the pointers, from 1/4 of the address space to 1/2 |
| for ( int i = NUMBER_OF_OBJECTS / 4; i < NUMBER_OF_OBJECTS / 2; i++ ) |
| { |
| Pointer<Object> pointer = pointers.get( i ); |
| mms.free( pointer ); |
| } |
| |
| // Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes |
| Pointer<Object> pointer1 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 ); |
| Assert.assertNotNull( pointer1 ); |
| |
| int pointerToSkip = NUMBER_OF_OBJECTS / 2 + NUMBER_OF_OBJECTS / 10; |
| for ( int i = NUMBER_OF_OBJECTS / 2; i < NUMBER_OF_OBJECTS * 3 / 4; i++ ) |
| { |
| // skip one pointer |
| if ( i == pointerToSkip ) |
| { |
| continue; |
| } |
| Pointer<Object> pointer = pointers.get( i ); |
| mms.free( pointer ); |
| } |
| |
| // Should NOT be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes |
| Pointer<Object> pointer2 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 ); |
| Assert.assertNull( pointer2 ); |
| |
| // Freeing the previously skipped pointer should then merge the whole memory space |
| mms.free( pointers.get( pointerToSkip ) ); |
| |
| // Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes |
| Pointer<Object> pointer3 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 ); |
| Assert.assertNotNull( pointer3 ); |
| |
| byte[] payload3 = MemoryTestUtils.generateRandomPayload( NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH ); |
| pointer3.getDirectBuffer().put( payload3 ); |
| byte[] retrievePayload3 = mms.retrieve( pointer3 ); |
| Assert.assertEquals( new String( payload3 ), new String( retrievePayload3 ) ); |
| |
| } |
| |
| @Test |
| public void testUpdate() |
| { |
| |
| final int NUMBER_OF_OBJECTS = 1; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| final byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| |
| final Pointer<Object> pointer = mms.store( payload ); |
| Assert.assertNotNull( pointer ); |
| Assert.assertEquals( new String( payload ), new String( mms.retrieve( pointer ) ) ); |
| |
| final byte[] otherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH ); |
| final Pointer<Object> otherPointer = mms.update( pointer, otherPayload ); |
| Assert.assertNotNull( otherPointer ); |
| Assert.assertEquals( pointer.getStart(), otherPointer.getStart() ); |
| Assert.assertEquals( pointer.getEnd(), otherPointer.getEnd() ); |
| Assert.assertEquals( new String( otherPayload ), new String( mms.retrieve( otherPointer ) ) ); |
| |
| final byte[] evenAnotherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH / 2 ); |
| final Pointer<Object> evenAnotherPointer = mms.update( otherPointer, evenAnotherPayload ); |
| Assert.assertNotNull( evenAnotherPointer ); |
| Assert.assertEquals( pointer.getStart(), evenAnotherPointer.getStart() ); |
| Assert.assertEquals( pointer.getEnd(), evenAnotherPointer.getEnd() ); |
| //Assert.assertEquals( 2, new String( mms.retrieve( evenAnotherPointer ) ).length() ); |
| Assert.assertTrue( new String( mms.retrieve( evenAnotherPointer ) ) |
| .startsWith( new String( evenAnotherPayload ) ) ); |
| |
| } |
| |
| |
| @Test |
| public void testAllocate() |
| { |
| |
| final int NUMBER_OF_OBJECTS = 10; |
| final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH; |
| |
| mms = instanciateMemoryManagerService( BUFFER_SIZE ); |
| |
| final byte[] payload1 = MemoryTestUtils.generateRandomPayload( 8 * SMALL_PAYLOAD_LENGTH ); |
| final Pointer<Object> pointer1 = mms.store( payload1 ); |
| Assert.assertNotNull( pointer1 ); |
| Assert.assertEquals( new String( payload1 ), new String( mms.retrieve( pointer1 ) ) ); |
| |
| final byte[] payload2 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH ); |
| final Pointer<Object> pointer2 = mms.store( payload2 ); |
| Assert.assertNotNull( pointer2 ); |
| Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) ); |
| |
| mms.free( pointer1 ); |
| |
| final byte[] payload3 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH ); |
| final Pointer<Object> pointer3 = mms.store( payload3 ); |
| Assert.assertNotNull( pointer3 ); |
| Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) ); |
| |
| final int size1 = 4 * SMALL_PAYLOAD_LENGTH; |
| final byte[] allocatedPayload1 = MemoryTestUtils.generateRandomPayload( size1 ); |
| final Pointer<Object> allocatedPointer1 = mms.allocate( Object.class, allocatedPayload1.length, -1, -1 ); |
| Assert.assertNotNull( allocatedPointer1 ); |
| final ByteBuffer buffer1 = allocatedPointer1.getDirectBuffer(); |
| Assert.assertNotNull( buffer1 ); |
| Assert.assertEquals( 0, buffer1.position() ); |
| Assert.assertEquals( size1, buffer1.limit() ); |
| Assert.assertEquals( size1, buffer1.capacity() ); |
| buffer1.put( allocatedPayload1 ); |
| Assert.assertEquals( new String( allocatedPayload1 ), new String( mms.retrieve( allocatedPointer1 ) ) ); |
| |
| final int size2 = 2 * SMALL_PAYLOAD_LENGTH; |
| final byte[] allocatedPayload2 = MemoryTestUtils.generateRandomPayload( size2 ); |
| final Pointer<Object> allocatedPointer2 = mms.allocate( Object.class, allocatedPayload2.length, -1, -1 ); |
| Assert.assertNotNull( allocatedPointer2 ); |
| final ByteBuffer buffer2 = allocatedPointer2.getDirectBuffer(); |
| Assert.assertNotNull( buffer2 ); |
| Assert.assertEquals( size2, buffer2.limit() ); |
| Assert.assertEquals( size2, buffer2.capacity() ); |
| buffer2.put( allocatedPayload2 ); |
| Assert.assertEquals( new String( allocatedPayload2 ), new String( mms.retrieve( allocatedPointer2 ) ) ); |
| |
| |
| // Ensure the new allocation has not overwritten other data |
| Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) ); |
| Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) ); |
| |
| } |
| |
| } |