diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index 3d7205b..999cff5 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -12,8 +12,6 @@ module tanya.container.buffer; import tanya.memory; -@nogc: - version (unittest) { private int fillBuffer(void* buffer, @@ -42,7 +40,6 @@ version (unittest) */ interface Buffer { -@nogc: /** * Returns: The size of the internal buffer. */ @@ -74,7 +71,6 @@ interface Buffer */ class ReadBuffer : Buffer { -@nogc: /// Internal buffer. protected ubyte[] _buffer; @@ -87,7 +83,7 @@ class ReadBuffer : Buffer /// Size by which the buffer will grow. protected immutable size_t blockSize; - private Allocator allocator; + private shared Allocator allocator; invariant { @@ -107,7 +103,7 @@ class ReadBuffer : Buffer */ this(size_t size = 8192, size_t minAvailable = 1024, - Allocator allocator = defaultAllocator) + shared Allocator allocator = defaultAllocator) { this.allocator = allocator; this.minAvailable = minAvailable; @@ -294,7 +290,6 @@ class ReadBuffer : Buffer */ class WriteBuffer : Buffer { -@nogc: /// Internal buffer. protected ubyte[] _buffer; @@ -310,7 +305,7 @@ class WriteBuffer : Buffer /// The position of the free area in the buffer. protected size_t position; - private Allocator allocator; + private shared Allocator allocator; invariant { @@ -325,7 +320,7 @@ class WriteBuffer : Buffer * will grow. */ this(size_t size = 8192, - Allocator allocator = defaultAllocator) + shared Allocator allocator = defaultAllocator) { this.allocator = allocator; blockSize = size; diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index e6e4ffc..e3bf25c 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -20,7 +20,6 @@ import tanya.memory; */ class SList(T) { -@nogc: /** * Creates a new $(D_PSYMBOL SList). * @@ -28,7 +27,7 @@ class SList(T) * allocator = The allocator should be used for the element * allocations. */ - this(Allocator allocator = defaultAllocator) + this(shared Allocator allocator = defaultAllocator) { this.allocator = allocator; reset(); @@ -241,7 +240,7 @@ class SList(T) * Params: * 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; size_t i; @@ -280,7 +279,7 @@ class SList(T) } /// Ditto. - int opApply(int delegate(ref T) @nogc dg) + int opApply(int delegate(ref T) dg) { int result; @@ -389,7 +388,7 @@ class SList(T) /// Current position in the list. protected Entry* position; - private Allocator allocator; + private shared Allocator allocator; } interface Stuff diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index 0890bcf..de28de2 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -20,7 +20,6 @@ import tanya.memory; */ class Queue(T) { -@nogc: /** * Creates a new $(D_PSYMBOL Queue). * @@ -28,7 +27,7 @@ class Queue(T) * allocator = The allocator should be used for the element * allocations. */ - this(Allocator allocator = defaultAllocator) + this(shared Allocator allocator = defaultAllocator) { this.allocator = allocator; } @@ -207,7 +206,7 @@ class Queue(T) /// The last element of the list. protected Entry* rear; - private Allocator allocator; + private shared Allocator allocator; } /// diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 4f66dda..fb8353b 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -12,8 +12,6 @@ module tanya.container.vector; import tanya.memory; -@nogc: - /** * One dimensional array. It allocates automatically if needed. * @@ -34,7 +32,6 @@ import tanya.memory; */ class Vector(T) { -@nogc: /** * Creates a new $(D_PSYMBOL Vector). * @@ -43,14 +40,14 @@ class Vector(T) * allocator = The allocator should be used for the element * allocations. */ - this(size_t length, Allocator allocator = defaultAllocator) + this(size_t length, shared Allocator allocator = defaultAllocator) { this.allocator = allocator; vector = makeArray!T(allocator, length); } /// Ditto. - this(Allocator allocator = defaultAllocator) + this(shared Allocator allocator = defaultAllocator) { this(0, allocator); } @@ -194,7 +191,7 @@ class Vector(T) * Params: * dg = $(D_KEYWORD foreach) body. */ - int opApply(int delegate(ref T) @nogc dg) + int opApply(int delegate(ref T) dg) { int result; @@ -211,7 +208,7 @@ class Vector(T) } /// 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; @@ -408,7 +405,7 @@ class Vector(T) /// Container. protected T[] vector; - private Allocator allocator; + private shared Allocator allocator; } /// diff --git a/source/tanya/crypto/cipher.d b/source/tanya/crypto/cipher.d index 60d1587..d436feb 100644 --- a/source/tanya/crypto/cipher.d +++ b/source/tanya/crypto/cipher.d @@ -43,7 +43,7 @@ enum Mode ubyte[] applyPadding(ref ubyte[] input, in Mode mode, in ushort blockSize, - Allocator allocator = defaultAllocator) + shared Allocator allocator = defaultAllocator) in { assert(blockSize > 0 && blockSize <= 256); @@ -204,7 +204,7 @@ unittest ref ubyte[] removePadding(ref ubyte[] input, in Mode mode, in ushort blockSize, - Allocator allocator = defaultAllocator) + shared Allocator allocator = defaultAllocator) in { assert(input.length != 0); diff --git a/source/tanya/event/internal/epoll.d b/source/tanya/event/internal/epoll.d index f4e99d9..0e76c3a 100644 --- a/source/tanya/event/internal/epoll.d +++ b/source/tanya/event/internal/epoll.d @@ -28,8 +28,6 @@ import core.sys.posix.netinet.in_; import core.time; import std.algorithm.comparison; -@nogc: - extern (C) nothrow { // TODO: Make a pull request for Phobos to mark this extern functions as @nogc. int epoll_create1(int __flags); @@ -42,7 +40,6 @@ private enum maxEvents = 128; class EpollLoop : Loop { -@nogc: /** * Initializes the loop. */ @@ -108,7 +105,7 @@ class EpollLoop : Loop * protocolFactory = Protocol factory. * socket = Socket. */ - protected override void acceptConnection(Protocol delegate() @nogc protocolFactory, + protected override void acceptConnection(Protocol delegate() protocolFactory, int socket) { sockaddr_in client_addr; diff --git a/source/tanya/event/internal/selector.d b/source/tanya/event/internal/selector.d index 9682a5d..36dca76 100644 --- a/source/tanya/event/internal/selector.d +++ b/source/tanya/event/internal/selector.d @@ -24,7 +24,6 @@ import core.sys.posix.unistd; */ class SocketTransport : DuplexTransport { -@nogc: private int socket_ = -1; private Protocol protocol_; diff --git a/source/tanya/event/loop.d b/source/tanya/event/loop.d index 4383845..73b0557 100644 --- a/source/tanya/event/loop.d +++ b/source/tanya/event/loop.d @@ -29,8 +29,6 @@ static if (UseEpoll) import tanya.event.internal.epoll; } -@nogc: - /** * Events. */ @@ -49,7 +47,6 @@ alias EventMask = BitFlags!Event; */ abstract class Loop { -@nogc: /// Pending watchers. protected Queue!Watcher pendings; @@ -204,7 +201,7 @@ abstract class Loop * protocolFactory = Protocol factory. * socket = Socket. */ - protected void acceptConnection(Protocol delegate() @nogc protocolFactory, + protected void acceptConnection(Protocol delegate() protocolFactory, int socket); /// Whether the event loop should be stopped. @@ -219,7 +216,6 @@ abstract class Loop */ class BadLoopException : Exception { -@nogc: /** * Params: * 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. */ 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); } diff --git a/source/tanya/event/transport.d b/source/tanya/event/transport.d index 7a3a60a..e1dc6f7 100644 --- a/source/tanya/event/transport.d +++ b/source/tanya/event/transport.d @@ -18,7 +18,6 @@ import tanya.event.protocol; */ class TransportException : Exception { -@nogc: /** * Params: * msg = Message to output. @@ -29,7 +28,7 @@ class TransportException : Exception this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) pure @safe nothrow const + Throwable next = null) pure @safe nothrow const @nogc { super(msg, file, line, next); } @@ -40,7 +39,6 @@ class TransportException : Exception */ interface Transport { -@nogc: /** * Returns: Protocol. */ @@ -78,7 +76,6 @@ interface Transport */ interface ReadTransport : Transport { -@nogc: /** * Returns: Underlying output buffer. */ @@ -103,7 +100,6 @@ interface ReadTransport : Transport */ interface WriteTransport : Transport { -@nogc: /** * Returns: Underlying input buffer. */ diff --git a/source/tanya/event/watcher.d b/source/tanya/event/watcher.d index f0ed9e1..e19a21b 100644 --- a/source/tanya/event/watcher.d +++ b/source/tanya/event/watcher.d @@ -21,7 +21,6 @@ import std.functional; */ abstract class Watcher { -@nogc: /// Whether the watcher is active. bool active; @@ -33,7 +32,6 @@ abstract class Watcher class ConnectionWatcher : Watcher { -@nogc: /// Watched file descriptor. private int socket_; @@ -41,7 +39,7 @@ class ConnectionWatcher : Watcher protected Protocol delegate() protocolFactory; /// Callback. - package void delegate(Protocol delegate() @nogc protocolFactory, + package void delegate(Protocol delegate() protocolFactory, int socket) accept; invariant @@ -54,27 +52,27 @@ class ConnectionWatcher : Watcher * protocolFactory = Function returning a new $(D_PSYMBOL Protocol) instance. * socket = Socket. */ - this(Protocol function() @nogc protocolFactory, int socket) + this(Protocol function() protocolFactory, int socket) { this.protocolFactory = toDelegate(protocolFactory); socket_ = socket; } /// Ditto. - this(Protocol delegate() @nogc protocolFactory, int socket) + this(Protocol delegate() protocolFactory, int socket) { this.protocolFactory = protocolFactory; socket_ = socket; } /// Ditto. - protected this(Protocol function() @nogc protocolFactory) + protected this(Protocol function() protocolFactory) { this.protocolFactory = toDelegate(protocolFactory); } /// Ditto. - protected this(Protocol delegate() @nogc protocolFactory) + protected this(Protocol delegate() protocolFactory) { this.protocolFactory = protocolFactory; } @@ -90,7 +88,7 @@ class ConnectionWatcher : Watcher /** * Returns: Application protocol factory. */ - @property inout(Protocol delegate() @nogc) protocol() inout + @property inout(Protocol delegate()) protocol() inout { return protocolFactory; } @@ -107,7 +105,6 @@ class ConnectionWatcher : Watcher */ class IOWatcher : ConnectionWatcher { -@nogc: /// References a watcher or a transport. DuplexTransport transport_; @@ -116,7 +113,7 @@ class IOWatcher : ConnectionWatcher * protocolFactory = Function returning application specific protocol. * transport = Transport. */ - this(Protocol delegate() @nogc protocolFactory, + this(Protocol delegate() protocolFactory, DuplexTransport transport) in { @@ -143,7 +140,7 @@ class IOWatcher : ConnectionWatcher * * Returns: $(D_KEYWORD this). */ - IOWatcher opCall(Protocol delegate() @nogc protocolFactory, + IOWatcher opCall(Protocol delegate() protocolFactory, DuplexTransport transport) @safe pure nothrow in { diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index c206826..07a19b9 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -10,49 +10,75 @@ */ module tanya.memory.allocator; +import std.experimental.allocator; +import std.traits; + /** - * This interface should be similar to $(D_PSYMBOL - * std.experimental.allocator.IAllocator), but usable in - * $(D_KEYWORD @nogc)-code. + * Allocator interface. */ interface Allocator { -@nogc: - /** - * Allocates $(D_PARAM s) bytes of memory. - * - * Params: - * s = Amount of memory to allocate. - * - * Returns: The pointer to the new allocated memory. - */ - void[] allocate(size_t s) @safe; + /** + * 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) shared; /** - * 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) @safe; + * 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; - /** - * 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 s) @safe; + /** + * 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) shared; - /** - * Static allocator instance and initializer. - * - * Returns: An $(D_PSYMBOL Allocator) instance. - */ - static @property Allocator instance() @safe; + /** + * Returns: The alignment offered. + */ + @property immutable(uint) alignment() shared const @safe pure nothrow; } + +/** + * 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; diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 67114df..4157fb2 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -26,8 +26,6 @@ else version (Posix) import core.sys.posix.pthread; } -@nogc: - version (Windows) { 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; } -@property Allocator defaultAllocator() @safe nothrow +@property shared(Allocator) defaultAllocator() @safe nothrow { 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) || hasElaborateDestructor!T || isDynamicArray!T; -private Allocator _defaultAllocator; +private shared Allocator _defaultAllocator; diff --git a/source/tanya/memory/ullocator.d b/source/tanya/memory/ullocator.d index 54af6b4..43761f7 100644 --- a/source/tanya/memory/ullocator.d +++ b/source/tanya/memory/ullocator.d @@ -7,28 +7,33 @@ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - */ + */ module tanya.memory.ullocator; import tanya.memory.allocator; +import core.atomic; +import core.exception; -@nogc: - -version (Posix): - -import core.sys.posix.sys.mman; -import core.sys.posix.unistd; +version (Posix) +{ + import core.stdc.errno; + import core.sys.posix.sys.mman; + 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). * 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 - * enought free blocks in the available regions. + * enough free blocks in the available regions. * Deallocation works in the same way. Deallocation doesn't immediately * 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. * * ---------------------------------------------------------------------------- @@ -42,382 +47,440 @@ import core.sys.posix.unistd; * | 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 { -@nogc: - @disable this(); + @disable this(); - shared static this() @safe nothrow - { - 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 + shared static this() { - immutable dataSize = addAlignment(size); - - void* data = findBlock(dataSize); - if (data is null) - { - data = initializeRegion(dataSize); - } - - return data is null ? null : data[0..size]; + version (Posix) + { + pageSize = sysconf(_SC_PAGE_SIZE); + } + else version (Windows) + { + SYSTEM_INFO si; + GetSystemInfo(&si); + 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. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) @trusted nothrow + * 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) shared @nogc @trusted nothrow { - if (p is null) - { - return true; - } + if (!size) + { + return null; + } + immutable dataSize = addAlignment(size); - 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; - } - return munmap(block.region, block.region.size) == 0; - } - else - { - block.free = true; - --block.region.blocks; - return true; - } + void* data = findBlock(dataSize); + if (data is null) + { + data = initializeRegion(dataSize); + } + + return data is null ? null : data[0..size]; } - /// - unittest - { - auto p = Ullocator.instance.allocate(20); + /// + @nogc @safe nothrow unittest + { + auto p = Ullocator.instance.allocate(20); - assert(Ullocator.instance.deallocate(p)); - } + assert(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; - } + Ullocator.instance.deallocate(p); + } - auto reallocP = allocate(size); - if (reallocP is null) - { - return false; - } + /** + * 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) 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) - { - if (size > p.length) - { - reallocP[0..p.length] = p[0..$]; - } - else - { - reallocP[0..size] = p[0..size]; - } - deallocate(p); - } - p = reallocP; + block1.free = false; + block2.free = true; - return true; - } + block2.size = block1.size - blockEntrySize - size; + block1.size = size; - /// - unittest - { - void[] p; - Ullocator.instance.reallocate(p, 10 * int.sizeof); - (cast(int[]) p)[7] = 123; + block2.region = block1.region; + atomicOp!"+="(block1.region.blocks, 1); + } + else + { + 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); - (cast(int[]) p)[15] = 8; + assert(Ullocator.instance.deallocate(p)); + } - assert(p.length == 80); - assert((cast(int[]) p)[15] == 8); - assert((cast(int[]) p)[7] == 123); + /** + * 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) 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); - assert((cast(int[]) p)[7] == 123); + if (p !is null) + { + 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. - * - * Returns: The global $(D_PSYMBOL Allocator) instance. - */ - static @property Ullocator instance() @trusted nothrow - { - if (instance_ is null) - { - immutable instanceSize = addAlignment(__traits(classInstanceSize, Ullocator)); + * + * Returns: Global $(D_PSYMBOL Ullocator) instance. + */ + static @property ref shared(Ullocator) instance() @nogc @trusted nothrow + { + if (instance_ is null) + { + immutable instanceSize = addAlignment(__traits(classInstanceSize, Ullocator)); - Region head; // Will become soon our region list head - void* data = initializeRegion(instanceSize, head); + Region head; // Will become soon our region list 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) - { - return null; - } - data[0..instanceSize] = typeid(Ullocator).initializer[]; - instance_ = cast(Ullocator) data; - instance_.head = head; - } - return instance_; - } + /// + @nogc @safe nothrow unittest + { + assert(instance is instance); + } - /// - unittest - { - assert(instance is instance); - } + /** + * Initializes a region for one element. + * + * 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; + } + } - /** - * Initializes a region for one element. - * - * 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; + region.blocks = 1; + region.size = regionSize; - Region region = cast(Region) p; - region.blocks = 1; - region.size = regionSize; + // Set the pointer to the head of the region list + if (head !is null) + { + head.prev = region; + } + region.next = head; + region.prev = null; + head = region; - // Set the pointer to the head of the region list - if (head !is null) - { - head.prev = region; - } - region.next = head; - region.prev = null; - head = region; + // Initialize the data block + void* memoryPointer = p + regionEntrySize; + Block block1 = cast(Block) memoryPointer; + block1.size = size; + block1.free = false; - // Initialize the data block - void* memoryPointer = p + regionEntrySize; - Block block1 = cast(Block) memoryPointer; - block1.size = size; - block1.free = false; + // It is what we want to return + void* data = memoryPointer + blockEntrySize; - // It is what we want to return - void* data = memoryPointer + blockEntrySize; + // Free block after 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; - // Free block after 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; + } - return data; - } + /// Ditto. + private void* initializeRegion(size_t size) shared @nogc nothrow + { + return initializeRegion(size, head); + } - /// Ditto. - private void* initializeRegion(size_t size) nothrow - { - return initializeRegion(size, head); - } + /** + * Params: + * x = Space to be aligned. + * + * 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: - * x = Space to be aligned. - * - * Returns: Aligned size of $(D_PARAM x). - */ - pragma(inline) - private static immutable(size_t) addAlignment(size_t x) @safe pure nothrow - out (result) - { - assert(result > 0); - } - body - { - return (x - 1) / alignment * alignment + alignment; - } + /** + * Params: + * x = Required space. + * + * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). + */ + pragma(inline) + private static immutable(size_t) calculateRegionSize(size_t x) + @nogc @safe pure nothrow + out (result) + { + assert(result > 0); + } + body + { + x += regionEntrySize + blockEntrySize * 2; + return x / pageSize * pageSize + pageSize; + } - /** - * Params: - * x = Required space. - * - * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). - */ - 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; - } + @property immutable(uint) alignment() shared const @nogc @safe pure nothrow + { + return alignment_; + } + private enum alignment_ = 8; - 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 - { - Region prev; - Region next; - uint blocks; - ulong size; - } - private alias Region = RegionEntry*; - private enum regionEntrySize = 32; + private shared Region head; - private Region head; - - private struct BlockEntry - { - Block prev; - Block next; - bool free; - ulong size; - Region region; - } - private alias Block = BlockEntry*; - private enum blockEntrySize = 40; + private shared struct BlockEntry + { + Block prev; + Block next; + bool free; + size_t size; + Region region; + } + private alias Block = shared BlockEntry*; + private enum blockEntrySize = 40; } diff --git a/source/tanya/random.d b/source/tanya/random.d index bf749c7..4e9197d 100644 --- a/source/tanya/random.d +++ b/source/tanya/random.d @@ -14,8 +14,6 @@ import tanya.memory; import std.digest.sha; import std.typecons; -@nogc: - /// Block size of entropy accumulator (SHA-512). enum blockSize = 64; @@ -27,7 +25,6 @@ enum maxGather = 128; */ class EntropyException : Exception { -@nogc: /** * Params: * msg = Message to output. @@ -38,7 +35,7 @@ class EntropyException : Exception this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) pure @safe nothrow const + Throwable next = null) pure @safe nothrow const @nogc { super(msg, file, line, next); } @@ -49,7 +46,6 @@ class EntropyException : Exception */ abstract class EntropySource { -@nogc: /// Amount of already generated entropy. protected ushort size_; @@ -103,7 +99,6 @@ version (linux) */ class PlatformEntropySource : EntropySource { - @nogc: /** * Returns: Minimum bytes required from the entropy source. */ @@ -164,13 +159,12 @@ version (linux) */ class Entropy { -@nogc: /// Entropy sources. protected EntropySource[] sources; private ubyte sourceCount_; - private Allocator allocator; + private shared Allocator allocator; /// Entropy accumulator. protected SHA!(maxGather * 8, 512) accumulator; @@ -181,7 +175,7 @@ class Entropy * allocator = Allocator to allocate entropy sources available on the * system. */ - this(size_t maxSources = 20, Allocator allocator = defaultAllocator) + this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator) in { assert(maxSources > 0 && maxSources <= ubyte.max);