Make allocators shared

This commit is contained in:
Eugen Wissner 2016-10-04 18:19:48 +02:00
parent be9181698a
commit 698660c4c8
14 changed files with 504 additions and 448 deletions

View File

@ -12,8 +12,6 @@ module tanya.container.buffer;
import tanya.memory; import tanya.memory;
@nogc:
version (unittest) version (unittest)
{ {
private int fillBuffer(void* buffer, private int fillBuffer(void* buffer,
@ -42,7 +40,6 @@ version (unittest)
*/ */
interface Buffer interface Buffer
{ {
@nogc:
/** /**
* Returns: The size of the internal buffer. * Returns: The size of the internal buffer.
*/ */
@ -74,7 +71,6 @@ interface Buffer
*/ */
class ReadBuffer : Buffer class ReadBuffer : Buffer
{ {
@nogc:
/// Internal buffer. /// Internal buffer.
protected ubyte[] _buffer; protected ubyte[] _buffer;
@ -87,7 +83,7 @@ class ReadBuffer : Buffer
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
protected immutable size_t blockSize; protected immutable size_t blockSize;
private Allocator allocator; private shared Allocator allocator;
invariant invariant
{ {
@ -107,7 +103,7 @@ class ReadBuffer : Buffer
*/ */
this(size_t size = 8192, this(size_t size = 8192,
size_t minAvailable = 1024, size_t minAvailable = 1024,
Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
this.minAvailable = minAvailable; this.minAvailable = minAvailable;
@ -294,7 +290,6 @@ class ReadBuffer : Buffer
*/ */
class WriteBuffer : Buffer class WriteBuffer : Buffer
{ {
@nogc:
/// Internal buffer. /// Internal buffer.
protected ubyte[] _buffer; protected ubyte[] _buffer;
@ -310,7 +305,7 @@ class WriteBuffer : Buffer
/// The position of the free area in the buffer. /// The position of the free area in the buffer.
protected size_t position; protected size_t position;
private Allocator allocator; private shared Allocator allocator;
invariant invariant
{ {
@ -325,7 +320,7 @@ class WriteBuffer : Buffer
* will grow. * will grow.
*/ */
this(size_t size = 8192, this(size_t size = 8192,
Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
blockSize = size; blockSize = size;

View File

@ -20,7 +20,6 @@ import tanya.memory;
*/ */
class SList(T) class SList(T)
{ {
@nogc:
/** /**
* Creates a new $(D_PSYMBOL SList). * Creates a new $(D_PSYMBOL SList).
* *
@ -28,7 +27,7 @@ class SList(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(Allocator allocator = defaultAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
reset(); reset();
@ -241,7 +240,7 @@ class SList(T)
* Params: * Params:
* dg = $(D_KEYWORD foreach) body. * dg = $(D_KEYWORD foreach) body.
*/ */
int opApply(int delegate(ref size_t i, ref T) @nogc dg) int opApply(int delegate(ref size_t i, ref T) dg)
{ {
int result; int result;
size_t i; size_t i;
@ -280,7 +279,7 @@ class SList(T)
} }
/// Ditto. /// Ditto.
int opApply(int delegate(ref T) @nogc dg) int opApply(int delegate(ref T) dg)
{ {
int result; int result;
@ -389,7 +388,7 @@ class SList(T)
/// Current position in the list. /// Current position in the list.
protected Entry* position; protected Entry* position;
private Allocator allocator; private shared Allocator allocator;
} }
interface Stuff interface Stuff

View File

@ -20,7 +20,6 @@ import tanya.memory;
*/ */
class Queue(T) class Queue(T)
{ {
@nogc:
/** /**
* Creates a new $(D_PSYMBOL Queue). * Creates a new $(D_PSYMBOL Queue).
* *
@ -28,7 +27,7 @@ class Queue(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(Allocator allocator = defaultAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
} }
@ -207,7 +206,7 @@ class Queue(T)
/// The last element of the list. /// The last element of the list.
protected Entry* rear; protected Entry* rear;
private Allocator allocator; private shared Allocator allocator;
} }
/// ///

View File

@ -12,8 +12,6 @@ module tanya.container.vector;
import tanya.memory; import tanya.memory;
@nogc:
/** /**
* One dimensional array. It allocates automatically if needed. * One dimensional array. It allocates automatically if needed.
* *
@ -34,7 +32,6 @@ import tanya.memory;
*/ */
class Vector(T) class Vector(T)
{ {
@nogc:
/** /**
* Creates a new $(D_PSYMBOL Vector). * Creates a new $(D_PSYMBOL Vector).
* *
@ -43,14 +40,14 @@ class Vector(T)
* allocator = The allocator should be used for the element * allocator = The allocator should be used for the element
* allocations. * allocations.
*/ */
this(size_t length, Allocator allocator = defaultAllocator) this(size_t length, shared Allocator allocator = defaultAllocator)
{ {
this.allocator = allocator; this.allocator = allocator;
vector = makeArray!T(allocator, length); vector = makeArray!T(allocator, length);
} }
/// Ditto. /// Ditto.
this(Allocator allocator = defaultAllocator) this(shared Allocator allocator = defaultAllocator)
{ {
this(0, allocator); this(0, allocator);
} }
@ -194,7 +191,7 @@ class Vector(T)
* Params: * Params:
* dg = $(D_KEYWORD foreach) body. * dg = $(D_KEYWORD foreach) body.
*/ */
int opApply(int delegate(ref T) @nogc dg) int opApply(int delegate(ref T) dg)
{ {
int result; int result;
@ -211,7 +208,7 @@ class Vector(T)
} }
/// Ditto. /// Ditto.
int opApply(int delegate(ref size_t i, ref T) @nogc dg) int opApply(int delegate(ref size_t i, ref T) dg)
{ {
int result; int result;
@ -408,7 +405,7 @@ class Vector(T)
/// Container. /// Container.
protected T[] vector; protected T[] vector;
private Allocator allocator; private shared Allocator allocator;
} }
/// ///

View File

@ -43,7 +43,7 @@ enum Mode
ubyte[] applyPadding(ref ubyte[] input, ubyte[] applyPadding(ref ubyte[] input,
in Mode mode, in Mode mode,
in ushort blockSize, in ushort blockSize,
Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
in in
{ {
assert(blockSize > 0 && blockSize <= 256); assert(blockSize > 0 && blockSize <= 256);
@ -204,7 +204,7 @@ unittest
ref ubyte[] removePadding(ref ubyte[] input, ref ubyte[] removePadding(ref ubyte[] input,
in Mode mode, in Mode mode,
in ushort blockSize, in ushort blockSize,
Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
in in
{ {
assert(input.length != 0); assert(input.length != 0);

View File

@ -28,8 +28,6 @@ import core.sys.posix.netinet.in_;
import core.time; import core.time;
import std.algorithm.comparison; import std.algorithm.comparison;
@nogc:
extern (C) nothrow extern (C) nothrow
{ // TODO: Make a pull request for Phobos to mark this extern functions as @nogc. { // TODO: Make a pull request for Phobos to mark this extern functions as @nogc.
int epoll_create1(int __flags); int epoll_create1(int __flags);
@ -42,7 +40,6 @@ private enum maxEvents = 128;
class EpollLoop : Loop class EpollLoop : Loop
{ {
@nogc:
/** /**
* Initializes the loop. * Initializes the loop.
*/ */
@ -108,7 +105,7 @@ class EpollLoop : Loop
* protocolFactory = Protocol factory. * protocolFactory = Protocol factory.
* socket = Socket. * socket = Socket.
*/ */
protected override void acceptConnection(Protocol delegate() @nogc protocolFactory, protected override void acceptConnection(Protocol delegate() protocolFactory,
int socket) int socket)
{ {
sockaddr_in client_addr; sockaddr_in client_addr;

View File

@ -24,7 +24,6 @@ import core.sys.posix.unistd;
*/ */
class SocketTransport : DuplexTransport class SocketTransport : DuplexTransport
{ {
@nogc:
private int socket_ = -1; private int socket_ = -1;
private Protocol protocol_; private Protocol protocol_;

View File

@ -29,8 +29,6 @@ static if (UseEpoll)
import tanya.event.internal.epoll; import tanya.event.internal.epoll;
} }
@nogc:
/** /**
* Events. * Events.
*/ */
@ -49,7 +47,6 @@ alias EventMask = BitFlags!Event;
*/ */
abstract class Loop abstract class Loop
{ {
@nogc:
/// Pending watchers. /// Pending watchers.
protected Queue!Watcher pendings; protected Queue!Watcher pendings;
@ -204,7 +201,7 @@ abstract class Loop
* protocolFactory = Protocol factory. * protocolFactory = Protocol factory.
* socket = Socket. * socket = Socket.
*/ */
protected void acceptConnection(Protocol delegate() @nogc protocolFactory, protected void acceptConnection(Protocol delegate() protocolFactory,
int socket); int socket);
/// Whether the event loop should be stopped. /// Whether the event loop should be stopped.
@ -219,7 +216,6 @@ abstract class Loop
*/ */
class BadLoopException : Exception class BadLoopException : Exception
{ {
@nogc:
/** /**
* Params: * Params:
* file = The file where the exception occurred. * file = The file where the exception occurred.
@ -227,7 +223,7 @@ class BadLoopException : Exception
* next = The previous exception in the chain of exceptions, if any. * next = The previous exception in the chain of exceptions, if any.
*/ */
this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) this(string file = __FILE__, size_t line = __LINE__, Throwable next = null)
pure @safe nothrow const pure @safe nothrow const @nogc
{ {
super("Event loop cannot be initialized.", file, line, next); super("Event loop cannot be initialized.", file, line, next);
} }

View File

@ -18,7 +18,6 @@ import tanya.event.protocol;
*/ */
class TransportException : Exception class TransportException : Exception
{ {
@nogc:
/** /**
* Params: * Params:
* msg = Message to output. * msg = Message to output.
@ -29,7 +28,7 @@ class TransportException : Exception
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) pure @safe nothrow const Throwable next = null) pure @safe nothrow const @nogc
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }
@ -40,7 +39,6 @@ class TransportException : Exception
*/ */
interface Transport interface Transport
{ {
@nogc:
/** /**
* Returns: Protocol. * Returns: Protocol.
*/ */
@ -78,7 +76,6 @@ interface Transport
*/ */
interface ReadTransport : Transport interface ReadTransport : Transport
{ {
@nogc:
/** /**
* Returns: Underlying output buffer. * Returns: Underlying output buffer.
*/ */
@ -103,7 +100,6 @@ interface ReadTransport : Transport
*/ */
interface WriteTransport : Transport interface WriteTransport : Transport
{ {
@nogc:
/** /**
* Returns: Underlying input buffer. * Returns: Underlying input buffer.
*/ */

View File

@ -21,7 +21,6 @@ import std.functional;
*/ */
abstract class Watcher abstract class Watcher
{ {
@nogc:
/// Whether the watcher is active. /// Whether the watcher is active.
bool active; bool active;
@ -33,7 +32,6 @@ abstract class Watcher
class ConnectionWatcher : Watcher class ConnectionWatcher : Watcher
{ {
@nogc:
/// Watched file descriptor. /// Watched file descriptor.
private int socket_; private int socket_;
@ -41,7 +39,7 @@ class ConnectionWatcher : Watcher
protected Protocol delegate() protocolFactory; protected Protocol delegate() protocolFactory;
/// Callback. /// Callback.
package void delegate(Protocol delegate() @nogc protocolFactory, package void delegate(Protocol delegate() protocolFactory,
int socket) accept; int socket) accept;
invariant invariant
@ -54,27 +52,27 @@ class ConnectionWatcher : Watcher
* protocolFactory = Function returning a new $(D_PSYMBOL Protocol) instance. * protocolFactory = Function returning a new $(D_PSYMBOL Protocol) instance.
* socket = Socket. * socket = Socket.
*/ */
this(Protocol function() @nogc protocolFactory, int socket) this(Protocol function() protocolFactory, int socket)
{ {
this.protocolFactory = toDelegate(protocolFactory); this.protocolFactory = toDelegate(protocolFactory);
socket_ = socket; socket_ = socket;
} }
/// Ditto. /// Ditto.
this(Protocol delegate() @nogc protocolFactory, int socket) this(Protocol delegate() protocolFactory, int socket)
{ {
this.protocolFactory = protocolFactory; this.protocolFactory = protocolFactory;
socket_ = socket; socket_ = socket;
} }
/// Ditto. /// Ditto.
protected this(Protocol function() @nogc protocolFactory) protected this(Protocol function() protocolFactory)
{ {
this.protocolFactory = toDelegate(protocolFactory); this.protocolFactory = toDelegate(protocolFactory);
} }
/// Ditto. /// Ditto.
protected this(Protocol delegate() @nogc protocolFactory) protected this(Protocol delegate() protocolFactory)
{ {
this.protocolFactory = protocolFactory; this.protocolFactory = protocolFactory;
} }
@ -90,7 +88,7 @@ class ConnectionWatcher : Watcher
/** /**
* Returns: Application protocol factory. * Returns: Application protocol factory.
*/ */
@property inout(Protocol delegate() @nogc) protocol() inout @property inout(Protocol delegate()) protocol() inout
{ {
return protocolFactory; return protocolFactory;
} }
@ -107,7 +105,6 @@ class ConnectionWatcher : Watcher
*/ */
class IOWatcher : ConnectionWatcher class IOWatcher : ConnectionWatcher
{ {
@nogc:
/// References a watcher or a transport. /// References a watcher or a transport.
DuplexTransport transport_; DuplexTransport transport_;
@ -116,7 +113,7 @@ class IOWatcher : ConnectionWatcher
* protocolFactory = Function returning application specific protocol. * protocolFactory = Function returning application specific protocol.
* transport = Transport. * transport = Transport.
*/ */
this(Protocol delegate() @nogc protocolFactory, this(Protocol delegate() protocolFactory,
DuplexTransport transport) DuplexTransport transport)
in in
{ {
@ -143,7 +140,7 @@ class IOWatcher : ConnectionWatcher
* *
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
IOWatcher opCall(Protocol delegate() @nogc protocolFactory, IOWatcher opCall(Protocol delegate() protocolFactory,
DuplexTransport transport) @safe pure nothrow DuplexTransport transport) @safe pure nothrow
in in
{ {

View File

@ -10,49 +10,75 @@
*/ */
module tanya.memory.allocator; module tanya.memory.allocator;
import std.experimental.allocator;
import std.traits;
/** /**
* This interface should be similar to $(D_PSYMBOL * Allocator interface.
* std.experimental.allocator.IAllocator), but usable in
* $(D_KEYWORD @nogc)-code.
*/ */
interface Allocator interface Allocator
{ {
@nogc: /**
/** * Allocates $(D_PARAM size) bytes of memory.
* Allocates $(D_PARAM s) bytes of memory. *
* * Params:
* Params: * size = Amount of memory to allocate.
* s = Amount of memory to allocate. *
* * Returns: The pointer to the new allocated memory.
* Returns: The pointer to the new allocated memory. */
*/ void[] allocate(size_t size) shared;
void[] allocate(size_t s) @safe;
/** /**
* Deallocates a memory block. * Deallocates a memory block.
* *
* Params: * Params:
* p = A pointer to the memory block to be freed. * p = A pointer to the memory block to be freed.
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) @safe; bool deallocate(void[] p) shared;
/** /**
* Increases or decreases the size of a memory block. * Increases or decreases the size of a memory block.
* *
* Params: * Params:
* p = A pointer to the memory block. * p = A pointer to the memory block.
* size = Size of the reallocated block. * size = Size of the reallocated block.
* *
* Returns: Whether the reallocation was successful. * Returns: Whether the reallocation was successful.
*/ */
bool reallocate(ref void[] p, size_t s) @safe; bool reallocate(ref void[] p, size_t size) shared;
/** /**
* Static allocator instance and initializer. * Returns: The alignment offered.
* */
* Returns: An $(D_PSYMBOL Allocator) instance. @property immutable(uint) alignment() shared const @safe pure nothrow;
*/
static @property Allocator instance() @safe;
} }
/**
* Params:
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
*
* Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could
* not be reallocated. In the latter
*/
bool resizeArray(T)(shared Allocator allocator,
ref T[] array,
in size_t length)
{
void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof))
{
return false;
}
array = cast(T[]) buf;
return true;
}
enum bool isFinalizable(T) = is(T == class) || is(T == interface)
|| hasElaborateDestructor!T || isDynamicArray!T;

View File

@ -26,8 +26,6 @@ else version (Posix)
import core.sys.posix.pthread; import core.sys.posix.pthread;
} }
@nogc:
version (Windows) version (Windows)
{ {
package alias Mutex = CRITICAL_SECTION; package alias Mutex = CRITICAL_SECTION;
@ -42,12 +40,12 @@ else version (Posix)
} }
} }
@property void defaultAllocator(Allocator allocator) @safe nothrow @property void defaultAllocator(shared Allocator allocator) @safe nothrow
{ {
_defaultAllocator = allocator; _defaultAllocator = allocator;
} }
@property Allocator defaultAllocator() @safe nothrow @property shared(Allocator) defaultAllocator() @safe nothrow
{ {
return _defaultAllocator; return _defaultAllocator;
} }
@ -204,4 +202,4 @@ bool resizeArray(T, A)(auto ref A allocator, ref T[] array, in size_t length)
enum bool isFinalizable(T) = is(T == class) || is(T == interface) enum bool isFinalizable(T) = is(T == class) || is(T == interface)
|| hasElaborateDestructor!T || isDynamicArray!T; || hasElaborateDestructor!T || isDynamicArray!T;
private Allocator _defaultAllocator; private shared Allocator _defaultAllocator;

View File

@ -7,28 +7,33 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.memory.ullocator; module tanya.memory.ullocator;
import tanya.memory.allocator; import tanya.memory.allocator;
import core.atomic;
import core.exception;
@nogc: version (Posix)
{
version (Posix): import core.stdc.errno;
import core.sys.posix.sys.mman;
import core.sys.posix.sys.mman; import core.sys.posix.unistd;
import core.sys.posix.unistd; }
else version (Windows)
{
import core.sys.windows.winbase;
import core.sys.windows.windows;
}
/** /**
* Allocator for Posix systems with mmap/munmap support.
*
* This allocator allocates memory in regions (multiple of 4 KB for example). * This allocator allocates memory in regions (multiple of 4 KB for example).
* Each region is then splitted in blocks. So it doesn't request the memory * Each region is then splitted in blocks. So it doesn't request the memory
* from the operating system on each call, but only if there are no large * from the operating system on each call, but only if there are no large
* enought free blocks in the available regions. * enough free blocks in the available regions.
* Deallocation works in the same way. Deallocation doesn't immediately * Deallocation works in the same way. Deallocation doesn't immediately
* gives the memory back to the operating system, but marks the appropriate * gives the memory back to the operating system, but marks the appropriate
* block as free and only if all blocks in the region are free, the complet * block as free and only if all blocks in the region are free, the complete
* region is deallocated. * region is deallocated.
* *
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
@ -42,382 +47,440 @@ import core.sys.posix.unistd;
* | N | -----------> next| || N | | | * | N | -----------> next| || N | | |
* | | | | | || | | | * | | | | | || | | |
* --------------------------------------------------- ------------------------ * --------------------------------------------------- ------------------------
*
* TODO:
* $(UL
* $(LI Thread safety (core.atomic.cas))
* $(LI If two neighbour blocks are free, they can be merged)
* $(LI Reallocation shoud check if there is enough free space in the
* next block instead of always moving the memory)
* $(LI Make 64 KB regions mininmal region size on Linux)
* )
*/ */
class Ullocator : Allocator class Ullocator : Allocator
{ {
@nogc: @disable this();
@disable this();
shared static this() @safe nothrow shared static this()
{
pageSize = sysconf(_SC_PAGE_SIZE);
}
/**
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
* size = Amount of memory to allocate.
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(size_t size) @trusted nothrow
{ {
immutable dataSize = addAlignment(size); version (Posix)
{
void* data = findBlock(dataSize); pageSize = sysconf(_SC_PAGE_SIZE);
if (data is null) }
{ else version (Windows)
data = initializeRegion(dataSize); {
} SYSTEM_INFO si;
GetSystemInfo(&si);
return data is null ? null : data[0..size]; pageSize = si.dwPageSize;
}
} }
///
unittest
{
auto p = Ullocator.instance.allocate(20);
assert(p);
Ullocator.instance.deallocate(p);
}
/**
* Search for a block large enough to keep $(D_PARAM size) and split it
* into two blocks if the block is too large.
*
* Params:
* size = Minimum size the block should have.
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(size_t size) nothrow
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
{
block1 = cast(Block) (cast(void*) r + regionEntrySize);
do
{
if (block1.free && block1.size >= size)
{
break RegionLoop;
}
}
while ((block1 = block1.next) !is null);
}
if (block1 is null)
{
return null;
}
else if (block1.size >= size + alignment + blockEntrySize)
{ // Split the block if needed
Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
block2.prev = block1;
if (block1.next is null)
{
block2.next = null;
}
else
{
block2.next = block1.next.next;
}
block1.next = block2;
block1.free = false;
block2.free = true;
block2.size = block1.size - blockEntrySize - size;
block1.size = size;
block2.region = block1.region;
++block1.region.blocks;
}
else
{
block1.free = false;
++block1.region.blocks;
}
return cast(void*) block1 + blockEntrySize;
}
/** /**
* Deallocates a memory block. * Allocates $(D_PARAM size) bytes of memory.
* *
* Params: * Params:
* p = A pointer to the memory block to be freed. * size = Amount of memory to allocate.
* *
* Returns: Whether the deallocation was successful. * Returns: The pointer to the new allocated memory.
*/ */
bool deallocate(void[] p) @trusted nothrow void[] allocate(size_t size) shared @nogc @trusted nothrow
{ {
if (p is null) if (!size)
{ {
return true; return null;
} }
immutable dataSize = addAlignment(size);
Block block = cast(Block) (p.ptr - blockEntrySize); void* data = findBlock(dataSize);
if (block.region.blocks <= 1) if (data is null)
{ {
if (block.region.prev !is null) data = initializeRegion(dataSize);
{ }
block.region.prev.next = block.region.next;
} return data is null ? null : data[0..size];
else // Replace the list head. It is being deallocated
{
head = block.region.next;
}
if (block.region.next !is null)
{
block.region.next.prev = block.region.prev;
}
return munmap(block.region, block.region.size) == 0;
}
else
{
block.free = true;
--block.region.blocks;
return true;
}
} }
/// ///
unittest @nogc @safe nothrow unittest
{ {
auto p = Ullocator.instance.allocate(20); auto p = Ullocator.instance.allocate(20);
assert(Ullocator.instance.deallocate(p)); assert(p);
}
/** Ullocator.instance.deallocate(p);
* Increases or decreases the size of a memory block. }
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, size_t size) @trusted nothrow
{
if (size == p.length)
{
return true;
}
auto reallocP = allocate(size); /**
if (reallocP is null) * Search for a block large enough to keep $(D_PARAM size) and split it
{ * into two blocks if the block is too large.
return false; *
} * Params:
* size = Minimum size the block should have.
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(size_t size) shared @nogc nothrow
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
{
block1 = cast(Block) (cast(void*) r + regionEntrySize);
do
{
if (block1.free && block1.size >= size)
{
break RegionLoop;
}
}
while ((block1 = block1.next) !is null);
}
if (block1 is null)
{
return null;
}
else if (block1.size >= size + alignment + blockEntrySize)
{ // Split the block if needed
Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
block2.prev = block1;
if (block1.next is null)
{
block2.next = null;
}
else
{
block2.next = block1.next.next;
}
block1.next = block2;
if (p !is null) block1.free = false;
{ block2.free = true;
if (size > p.length)
{
reallocP[0..p.length] = p[0..$];
}
else
{
reallocP[0..size] = p[0..size];
}
deallocate(p);
}
p = reallocP;
return true; block2.size = block1.size - blockEntrySize - size;
} block1.size = size;
/// block2.region = block1.region;
unittest atomicOp!"+="(block1.region.blocks, 1);
{ }
void[] p; else
Ullocator.instance.reallocate(p, 10 * int.sizeof); {
(cast(int[]) p)[7] = 123; block1.free = false;
atomicOp!"+="(block1.region.blocks, 1);
}
return cast(void*) block1 + blockEntrySize;
}
assert(p.length == 40); /**
* Deallocates a memory block.
*
* Params:
* p = A pointer to the memory block to be freed.
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared @nogc @trusted nothrow
{
if (p is null)
{
return true;
}
Ullocator.instance.reallocate(p, 8 * int.sizeof); Block block = cast(Block) (p.ptr - blockEntrySize);
if (block.region.blocks <= 1)
{
if (block.region.prev !is null)
{
block.region.prev.next = block.region.next;
}
else // Replace the list head. It is being deallocated
{
head = block.region.next;
}
if (block.region.next !is null)
{
block.region.next.prev = block.region.prev;
}
version (Posix)
{
return munmap(cast(void*) block.region, block.region.size) == 0;
}
version (Windows)
{
return VirtualFree(cast(void*) block.region, 0, MEM_RELEASE) == 0;
}
}
else
{
block.free = true;
atomicOp!"-="(block.region.blocks, 1);
return true;
}
}
assert(p.length == 32); ///
assert((cast(int[]) p)[7] == 123); @nogc @safe nothrow unittest
{
auto p = Ullocator.instance.allocate(20);
Ullocator.instance.reallocate(p, 20 * int.sizeof); assert(Ullocator.instance.deallocate(p));
(cast(int[]) p)[15] = 8; }
assert(p.length == 80); /**
assert((cast(int[]) p)[15] == 8); * Increases or decreases the size of a memory block.
assert((cast(int[]) p)[7] == 123); *
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, size_t size) shared @nogc @trusted nothrow
{
void[] reallocP;
Ullocator.instance.reallocate(p, 8 * int.sizeof); if (size == p.length)
{
return true;
}
else if (size > 0)
{
reallocP = allocate(size);
if (reallocP is null)
{
return false;
}
}
assert(p.length == 32); if (p !is null)
assert((cast(int[]) p)[7] == 123); {
if (size > p.length)
{
reallocP[0..p.length] = p[0..$];
}
else if (size > 0)
{
reallocP[0..size] = p[0..size];
}
deallocate(p);
}
p = reallocP;
Ullocator.instance.deallocate(p); return true;
} }
/** ///
@nogc @safe nothrow unittest
{
void[] p;
Ullocator.instance.reallocate(p, 10 * int.sizeof);
(cast(int[]) p)[7] = 123;
assert(p.length == 40);
Ullocator.instance.reallocate(p, 8 * int.sizeof);
assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
Ullocator.instance.reallocate(p, 20 * int.sizeof);
(cast(int[]) p)[15] = 8;
assert(p.length == 80);
assert((cast(int[]) p)[15] == 8);
assert((cast(int[]) p)[7] == 123);
Ullocator.instance.reallocate(p, 8 * int.sizeof);
assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
Ullocator.instance.deallocate(p);
}
/**
* Static allocator instance and initializer. * Static allocator instance and initializer.
* *
* Returns: The global $(D_PSYMBOL Allocator) instance. * Returns: Global $(D_PSYMBOL Ullocator) instance.
*/ */
static @property Ullocator instance() @trusted nothrow static @property ref shared(Ullocator) instance() @nogc @trusted nothrow
{ {
if (instance_ is null) if (instance_ is null)
{ {
immutable instanceSize = addAlignment(__traits(classInstanceSize, Ullocator)); immutable instanceSize = addAlignment(__traits(classInstanceSize, Ullocator));
Region head; // Will become soon our region list head Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head); void* data = initializeRegion(instanceSize, head);
if (data !is null)
{
data[0..instanceSize] = typeid(Ullocator).initializer[];
instance_ = cast(shared Ullocator) data;
instance_.head = head;
}
}
return instance_;
}
if (data is null) ///
{ @nogc @safe nothrow unittest
return null; {
} assert(instance is instance);
data[0..instanceSize] = typeid(Ullocator).initializer[]; }
instance_ = cast(Ullocator) data;
instance_.head = head;
}
return instance_;
}
/// /**
unittest * Initializes a region for one element.
{ *
assert(instance is instance); * Params:
} * size = Aligned size of the first data block in the region.
* head = Region list head.
*
* Returns: A pointer to the data.
*/
pragma(inline)
private static void* initializeRegion(size_t size,
ref Region head) @nogc nothrow
{
immutable regionSize = calculateRegionSize(size);
version (Posix)
{
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
{
if (errno == ENOMEM)
{
onOutOfMemoryError();
}
return null;
}
}
else version (Windows)
{
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
if (p is null)
{
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
onOutOfMemoryError();
}
return null;
}
}
/** Region region = cast(Region) p;
* Initializes a region for one element. region.blocks = 1;
* region.size = regionSize;
* Params:
* size = Aligned size of the first data block in the region.
* head = Region list head.
*
* Returns: A pointer to the data.
*/
pragma(inline)
private static void* initializeRegion(size_t size,
ref Region head) nothrow
{
immutable regionSize = calculateRegionSize(size);
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
{
return null;
}
Region region = cast(Region) p; // Set the pointer to the head of the region list
region.blocks = 1; if (head !is null)
region.size = regionSize; {
head.prev = region;
}
region.next = head;
region.prev = null;
head = region;
// Set the pointer to the head of the region list // Initialize the data block
if (head !is null) void* memoryPointer = p + regionEntrySize;
{ Block block1 = cast(Block) memoryPointer;
head.prev = region; block1.size = size;
} block1.free = false;
region.next = head;
region.prev = null;
head = region;
// Initialize the data block // It is what we want to return
void* memoryPointer = p + regionEntrySize; void* data = memoryPointer + blockEntrySize;
Block block1 = cast(Block) memoryPointer;
block1.size = size;
block1.free = false;
// It is what we want to return // Free block after data
void* data = memoryPointer + blockEntrySize; memoryPointer = data + size;
Block block2 = cast(Block) memoryPointer;
block1.prev = block2.next = null;
block1.next = block2;
block2.prev = block1;
block2.size = regionSize - size - regionEntrySize - blockEntrySize * 2;
block2.free = true;
block1.region = block2.region = region;
// Free block after data return data;
memoryPointer = data + size; }
Block block2 = cast(Block) memoryPointer;
block1.prev = block2.next = null;
block1.next = block2;
block2.prev = block1;
block2.size = regionSize - size - regionEntrySize - blockEntrySize * 2;
block2.free = true;
block1.region = block2.region = region;
return data; /// Ditto.
} private void* initializeRegion(size_t size) shared @nogc nothrow
{
return initializeRegion(size, head);
}
/// Ditto. /**
private void* initializeRegion(size_t size) nothrow * Params:
{ * x = Space to be aligned.
return initializeRegion(size, head); *
} * Returns: Aligned size of $(D_PARAM x).
*/
pragma(inline)
private static immutable(size_t) addAlignment(size_t x)
@nogc @safe pure nothrow
out (result)
{
assert(result > 0);
}
body
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
/** /**
* Params: * Params:
* x = Space to be aligned. * x = Required space.
* *
* Returns: Aligned size of $(D_PARAM x). * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/ */
pragma(inline) pragma(inline)
private static immutable(size_t) addAlignment(size_t x) @safe pure nothrow private static immutable(size_t) calculateRegionSize(size_t x)
out (result) @nogc @safe pure nothrow
{ out (result)
assert(result > 0); {
} assert(result > 0);
body }
{ body
return (x - 1) / alignment * alignment + alignment; {
} x += regionEntrySize + blockEntrySize * 2;
return x / pageSize * pageSize + pageSize;
}
/** @property immutable(uint) alignment() shared const @nogc @safe pure nothrow
* Params: {
* x = Required space. return alignment_;
* }
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). private enum alignment_ = 8;
*/
pragma(inline)
private static immutable(size_t) calculateRegionSize(size_t x)
@safe pure nothrow
out (result)
{
assert(result > 0);
}
body
{
x += regionEntrySize + blockEntrySize * 2;
return x / pageSize * pageSize + pageSize;
}
enum alignment = 8; private shared static Ullocator instance_;
private static Ullocator instance_; private shared static immutable size_t pageSize;
private shared static immutable long pageSize; private shared struct RegionEntry
{
Region prev;
Region next;
uint blocks;
size_t size;
}
private alias Region = shared RegionEntry*;
private enum regionEntrySize = 32;
private struct RegionEntry private shared Region head;
{
Region prev;
Region next;
uint blocks;
ulong size;
}
private alias Region = RegionEntry*;
private enum regionEntrySize = 32;
private Region head; private shared struct BlockEntry
{
private struct BlockEntry Block prev;
{ Block next;
Block prev; bool free;
Block next; size_t size;
bool free; Region region;
ulong size; }
Region region; private alias Block = shared BlockEntry*;
} private enum blockEntrySize = 40;
private alias Block = BlockEntry*;
private enum blockEntrySize = 40;
} }

View File

@ -14,8 +14,6 @@ import tanya.memory;
import std.digest.sha; import std.digest.sha;
import std.typecons; import std.typecons;
@nogc:
/// Block size of entropy accumulator (SHA-512). /// Block size of entropy accumulator (SHA-512).
enum blockSize = 64; enum blockSize = 64;
@ -27,7 +25,6 @@ enum maxGather = 128;
*/ */
class EntropyException : Exception class EntropyException : Exception
{ {
@nogc:
/** /**
* Params: * Params:
* msg = Message to output. * msg = Message to output.
@ -38,7 +35,7 @@ class EntropyException : Exception
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) pure @safe nothrow const Throwable next = null) pure @safe nothrow const @nogc
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }
@ -49,7 +46,6 @@ class EntropyException : Exception
*/ */
abstract class EntropySource abstract class EntropySource
{ {
@nogc:
/// Amount of already generated entropy. /// Amount of already generated entropy.
protected ushort size_; protected ushort size_;
@ -103,7 +99,6 @@ version (linux)
*/ */
class PlatformEntropySource : EntropySource class PlatformEntropySource : EntropySource
{ {
@nogc:
/** /**
* Returns: Minimum bytes required from the entropy source. * Returns: Minimum bytes required from the entropy source.
*/ */
@ -164,13 +159,12 @@ version (linux)
*/ */
class Entropy class Entropy
{ {
@nogc:
/// Entropy sources. /// Entropy sources.
protected EntropySource[] sources; protected EntropySource[] sources;
private ubyte sourceCount_; private ubyte sourceCount_;
private Allocator allocator; private shared Allocator allocator;
/// Entropy accumulator. /// Entropy accumulator.
protected SHA!(maxGather * 8, 512) accumulator; protected SHA!(maxGather * 8, 512) accumulator;
@ -181,7 +175,7 @@ class Entropy
* allocator = Allocator to allocate entropy sources available on the * allocator = Allocator to allocate entropy sources available on the
* system. * system.
*/ */
this(size_t maxSources = 20, Allocator allocator = defaultAllocator) this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(maxSources > 0 && maxSources <= ubyte.max); assert(maxSources > 0 && maxSources <= ubyte.max);