Skip to content

Commit

Permalink
Issue #50 Implement offheap-resource provider
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdennis committed Mar 22, 2016
1 parent 093cc12 commit b2c3f22
Show file tree
Hide file tree
Showing 8 changed files with 582 additions and 17 deletions.
6 changes: 6 additions & 0 deletions offheap-resource/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
<artifactId>offheap-resource</artifactId>

<dependencies>
<dependency>
<!-- This is dep is probably wrong -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.18</version>
</dependency>
<dependency>
<groupId>org.terracotta.internal</groupId>
<artifactId>tc-config-parser</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Covered Software is OffHeap Resource.
*
* The Initial Developer of the Covered Software is
* Terracotta, Inc., a Software AG company
*/

package org.terracotta.offheapresource;

import java.util.concurrent.atomic.AtomicLong;

/**
* Represents an offheap resource, providing a reservation system that can be
* used to control the combined memory usage of participating consumers.
* <p>
* Reservation and release calls perform no allocations, and therefore rely on
* the cooperation of callers to achieve control over the 'real' resource usage.
*/
public class OffHeapResource {

private final AtomicLong remaining;

/**
* Creates a resource of the given initial size.
*
* @param size size of the resource
* @throws IllegalArgumentException if the size is negative
*/
OffHeapResource(long size) throws IllegalArgumentException {
if (size < 0) {
throw new IllegalArgumentException("Resource size cannot be negative");
} else {
this.remaining = new AtomicLong(size);
}
}

/**
* Reserves the given amount of this resource.
* <p>
* This method <em>performs no allocation</em>. It is simply a reservation
* that the consumer agrees to bind by the result of. A {@code false} return
* should mean the caller refrains from performing any associated allocation.
*
* @param size reservation size
* @return {@code true} if the reservation succeeded
* @throws IllegalArgumentException if the reservation size is negative
*/
public boolean reserve(long size) throws IllegalArgumentException {
if (size < 0) {
throw new IllegalArgumentException("Reservation size cannot be negative");
} else {
for (long current = remaining.get(); current >= size; current = remaining.get()) {
if (remaining.compareAndSet(current, current - size)) {
return true;
}
}
return false;
}
}

/**
* Releases the given amount of resource back to this pool.
*
* @param size release size
* @throws IllegalArgumentException if the release size is negative
*/
public void release(long size) throws IllegalArgumentException {
if (size < 0) {
throw new IllegalArgumentException("Released size cannot be negative");
} else {
remaining.addAndGet(size);
}
}

/**
* Returns the size of the remaining resource that can be reserved.
*
* @return the remaining resource size
*/
public long available() {
return remaining.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Covered Software is OffHeap Resource.
*
* The Initial Developer of the Covered Software is
* Terracotta, Inc., a Software AG company
*/

package org.terracotta.offheapresource;

import org.terracotta.entity.ServiceConfiguration;

/**
*
* @author cdennis
*/
public final class OffHeapResourceIdentifier implements ServiceConfiguration<OffHeapResource> {

private final String name;

public static OffHeapResourceIdentifier identifier(String name) {
return new OffHeapResourceIdentifier(name);
}

private OffHeapResourceIdentifier(String name) {
if (name == null) {
throw new NullPointerException("Name cannot be null");
} else {
this.name = name;
}
}

@Override
public int hashCode() {
return name.hashCode();
}

@Override
public boolean equals(Object obj) {
return (obj instanceof OffHeapResourceIdentifier) && name.equals(((OffHeapResourceIdentifier) obj).name);
}

@Override
public Class<OffHeapResource> getServiceType() {
return OffHeapResource.class;
}
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,109 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Covered Software is OffHeap Resource.
*
* The Initial Developer of the Covered Software is
* Terracotta, Inc., a Software AG company
*/

package org.terracotta.offheapresource;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.terracotta.entity.ServiceConfiguration;
import org.terracotta.entity.ServiceProvider;
import org.terracotta.entity.ServiceProviderCleanupException;
import org.terracotta.entity.ServiceProviderConfiguration;
import org.terracotta.offheapresource.config.MemoryUnit;
import org.terracotta.offheapresource.config.ResourceType;

/**
*
* @author cdennis
* A provider of {@link OffHeapResource} instances.
* <p>
* This service allows for the configuration of a multitude of virtual offheap
* resource pools from which participating entities can reserve space. This
* allows for the partitioning and control of memory usage by entities
* consuming this service.
*/
class OffHeapResourcesProvider implements ServiceProvider {
public class OffHeapResourcesProvider implements ServiceProvider {

private static final Logger LOGGER = LoggerFactory.getLogger(OffHeapResourcesProvider.class);

private final Map<OffHeapResourceIdentifier, OffHeapResource> resources = new HashMap<OffHeapResourceIdentifier, OffHeapResource>();

@Override
public boolean initialize(ServiceProviderConfiguration configuration) {
throw new UnsupportedOperationException();
public synchronized boolean initialize(ServiceProviderConfiguration unknownConfig) {
if (unknownConfig instanceof OffHeapResourcesConfiguration) {
OffHeapResourcesConfiguration configuration = (OffHeapResourcesConfiguration) unknownConfig;
if (resources.isEmpty()) {
long totalSize = 0;
for (ResourceType r : configuration.getResources()) {
long size = convert(r.getValue(), r.getUnit()).longValueExact();
totalSize += size;
resources.put(OffHeapResourceIdentifier.identifier(r.getName()), new OffHeapResource(size));
}
Long physicalMemory = PhysicalMemory.totalPhysicalMemory();
if (physicalMemory != null && totalSize > physicalMemory) {
LOGGER.warn("More offheap configured than there is physical memory [{} > {}]", totalSize, physicalMemory);
}
return true;
} else {
throw new IllegalStateException("Resources already initialized");
}
} else {
return false;
}
}

@Override
public <T> T getService(long consumerID, ServiceConfiguration<T> configuration) {
throw new UnsupportedOperationException();
public <T> T getService(long consumerID, ServiceConfiguration<T> unknownConfiguration) {
if (unknownConfiguration instanceof OffHeapResourceIdentifier) {
OffHeapResourceIdentifier identifier = (OffHeapResourceIdentifier) unknownConfiguration;
return (T) identifier.getServiceType().cast(resources.get(identifier));
} else {
throw new IllegalArgumentException("Unexpected configuration type " + unknownConfiguration.getClass());
}
}

@Override
public Collection<Class<?>> getProvidedServiceTypes() {
throw new UnsupportedOperationException();
return Collections.<Class<?>>singleton(OffHeapResource.class);
}

@Override
public void close() throws IOException {
throw new UnsupportedOperationException();
public void close() {
clear();
}

@Override
public void clear() throws ServiceProviderCleanupException {
throw new UnsupportedOperationException();
public void clear() {
resources.clear();
}

private static BigInteger convert(BigInteger value, MemoryUnit unit) {
switch (unit) {
case B: return value.shiftLeft(0);
case K_B: return value.shiftLeft(10);
case MB: return value.shiftLeft(20);
case GB: return value.shiftLeft(30);
case TB: return value.shiftLeft(40);
case PB: return value.shiftLeft(50);
}
throw new IllegalArgumentException("Unknown unit " + unit);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.terracotta.offheapresource;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.InvocationTargetException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author cdennis
*/
class PhysicalMemory {

private static final Logger LOGGER = LoggerFactory.getLogger(PhysicalMemory.class);
private static final OperatingSystemMXBean OS_BEAN = ManagementFactory.getOperatingSystemMXBean();

public static Long totalPhysicalMemory() {
return getAttribute("getTotalPhysicalMemorySize");
}

public static Long freePhysicalMemory() {
return getAttribute("getFreePhysicalMemorySize");
}

public static Long totalSwapSpace() {
return getAttribute("getTotalSwapSpaceSize");
}

public static Long freeSwapSpace() {
return getAttribute("getFreeSwapSpaceSize");
}

public static Long ourCommittedVirtualMemory() {
return getAttribute("getCommittedVirtualMemorySize");
}

private static <T> T getAttribute(String name) {
LOGGER.trace("Bean lookup for {}", name);
for (Class<?> s = OS_BEAN.getClass(); s != null; s = s.getSuperclass()) {
try {
T result = (T) s.getMethod(name).invoke(OS_BEAN);
LOGGER.trace("Bean lookup successful using {}, got {}", s, result);
return result;
} catch (SecurityException e) {
LOGGER.trace("Bean lookup failed on {}", s, e);
} catch (NoSuchMethodException e) {
LOGGER.trace("Bean lookup failed on {}", s, e);
} catch (IllegalAccessException e) {
LOGGER.trace("Bean lookup failed on {}", s, e);
} catch (IllegalArgumentException e) {
LOGGER.trace("Bean lookup failed on {}", s, e);
} catch (InvocationTargetException e) {
LOGGER.trace("Bean lookup failed on {}", s, e);
}
}
for (Class<?> i : OS_BEAN.getClass().getInterfaces()) {
try {
T result = (T) i.getMethod(name).invoke(OS_BEAN);
LOGGER.trace("Bean lookup successful using {}, got {}", i, result);
return result;
} catch (SecurityException e) {
LOGGER.trace("Bean lookup failed on {}", i, e);
} catch (NoSuchMethodException e) {
LOGGER.trace("Bean lookup failed on {}", i, e);
} catch (IllegalAccessException e) {
LOGGER.trace("Bean lookup failed on {}", i, e);
} catch (IllegalArgumentException e) {
LOGGER.trace("Bean lookup failed on {}", i, e);
} catch (InvocationTargetException e) {
LOGGER.trace("Bean lookup failed on {}", i, e);
}
}
LOGGER.trace("Returning null for {}", name);
return null;
}
}
Loading

0 comments on commit b2c3f22

Please sign in to comment.