-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #50 Implement offheap-resource provider
- Loading branch information
1 parent
093cc12
commit b2c3f22
Showing
8 changed files
with
582 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceIdentifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
98 changes: 81 additions & 17 deletions
98
offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
offheap-resource/src/main/java/org/terracotta/offheapresource/PhysicalMemory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.