diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/HugeAllocator.cxx | 87 | ||||
-rw-r--r-- | src/util/HugeAllocator.hxx | 82 | ||||
-rw-r--r-- | src/util/SliceBuffer.hxx | 161 | ||||
-rw-r--r-- | src/util/bit_reverse.h | 5 | ||||
-rw-r--r-- | src/util/list.h | 49 |
5 files changed, 358 insertions, 26 deletions
diff --git a/src/util/HugeAllocator.cxx b/src/util/HugeAllocator.cxx new file mode 100644 index 000000000..d1c55c965 --- /dev/null +++ b/src/util/HugeAllocator.cxx @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "HugeAllocator.hxx" + +#ifdef __linux__ +#include <sys/mman.h> +#include <unistd.h> +#else +#include <stdlib.h> +#endif + +#ifdef __linux__ + +/** + * Round up the parameter, make it page-aligned. + */ +gcc_const +static size_t +AlignToPageSize(size_t size) +{ + static const long page_size = sysconf(_SC_PAGESIZE); + if (page_size > 0) + return size; + + size_t ps(page_size); + return (size + ps - 1) / ps * ps; +} + +void * +HugeAllocate(size_t size) +{ + size = AlignToPageSize(size); + + constexpr int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE; + void *p = mmap(nullptr, size, + PROT_READ|PROT_WRITE, flags, + -1, 0); + if (p == (void *)-1) + return nullptr; + +#ifdef MADV_HUGEPAGE + /* allow the Linux kernel to use "Huge Pages", which reduces page + table overhead for this big chunk of data */ + madvise(p, size, MADV_HUGEPAGE); +#endif + +#ifdef MADV_DONTFORK + /* just in case MPD needs to fork, don't copy this allocation + to the child process, to reduce overhead */ + madvise(p, size, MADV_DONTFORK); +#endif + + return p; +} + +void +HugeFree(void *p, size_t size) +{ + munmap(p, AlignToPageSize(size)); +} + +void +HugeDiscard(void *p, size_t size) +{ +#ifdef MADV_DONTNEED + madvise(p, AlignToPageSize(size), MADV_DONTNEED); +#endif +} + +#endif diff --git a/src/util/HugeAllocator.hxx b/src/util/HugeAllocator.hxx new file mode 100644 index 000000000..01c92cd43 --- /dev/null +++ b/src/util/HugeAllocator.hxx @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_HUGE_ALLOCATOR_HXX +#define MPD_HUGE_ALLOCATOR_HXX + +#include "gcc.h" + +#include <stddef.h> + +#ifdef __linux__ + +/** + * Allocate a huge amount of memory. This will be done in a way that + * allows giving the memory back to the kernel as soon as we don't + * need it anymore. On the downside, this call is expensive. + */ +gcc_malloc +void * +HugeAllocate(size_t size); + +/** + * @param p an allocation returned by HugeAllocate() + * @param size the allocation's size as passed to HugeAllocate() + */ +void +HugeFree(void *p, size_t size); + +/** + * Discard any data stored in the allocation and give the memory back + * to the kernel. After returning, the allocation still exists and + * can be reused at any time, but its contents are undefined. + * + * @param p an allocation returned by HugeAllocate() + * @param size the allocation's size as passed to HugeAllocate() + */ +void +HugeDiscard(void *p, size_t size); + +#else + +/* not Linux: fall back to standard C calls */ + +#include <stdlib.h> + +gcc_malloc +static inline void * +HugeAllocate(size_t size) +{ + return malloc(size); +} + +static inline void +HugeFree(void *p, size_t) +{ + free(p); +} + +static inline void +HugeDiscard(void *, size_t) +{ +} + +#endif + +#endif diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx new file mode 100644 index 000000000..c61f164f4 --- /dev/null +++ b/src/util/SliceBuffer.hxx @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SLICE_BUFFER_HXX +#define MPD_SLICE_BUFFER_HXX + +#include "HugeAllocator.hxx" +#include "gcc.h" + +#include <utility> +#include <new> + +#include <assert.h> +#include <stddef.h> + +/** + * This class pre-allocates a certain number of objects, and allows + * callers to allocate and free these objects ("slices"). + */ +template<typename T> +class SliceBuffer { + union Slice { + Slice *next; + + T value; + }; + + /** + * The maximum number of slices in this container. + */ + const unsigned n_max; + + /** + * The number of slices that are initialized. This is used to + * avoid page faulting on the new allocation, so the kernel + * does not need to reserve physical memory pages. + */ + unsigned n_initialized; + + /** + * The number of slices currently allocated. + */ + unsigned n_allocated; + + Slice *const data; + + /** + * Pointer to the first free element in the chain. + */ + Slice *available; + + size_t CalcAllocationSize() const { + return n_max * sizeof(Slice); + } + +public: + SliceBuffer(unsigned _count) + :n_max(_count), n_initialized(0), n_allocated(0), + data((Slice *)HugeAllocate(CalcAllocationSize())), + available(nullptr) { + assert(n_max > 0); + } + + ~SliceBuffer() { + /* all slices must be freed explicitly, and this + assertion checks for leaks */ + assert(n_allocated == 0); + + HugeFree(data, CalcAllocationSize()); + } + + SliceBuffer(const SliceBuffer &other) = delete; + SliceBuffer &operator=(const SliceBuffer &other) = delete; + + /** + * @return true if buffer allocation (by the constructor) has failed + */ + bool IsOOM() { + return data == nullptr; + } + + unsigned GetCapacity() const { + return n_max; + } + + bool IsEmpty() const { + return n_allocated == 0; + } + + bool IsFull() const { + return n_allocated == n_max; + } + + template<typename... Args> + T *Allocate(Args&&... args) { + assert(n_initialized <= n_max); + assert(n_allocated <= n_initialized); + + if (available == nullptr) { + if (n_initialized == n_max) { + /* out of (internal) memory, buffer is full */ + assert(n_allocated == n_max); + return nullptr; + } + + available = &data[n_initialized++]; + available->next = nullptr; + } + + /* allocate a slice */ + T *value = &available->value; + available = available->next; + ++n_allocated; + + /* construct the object */ + return ::new((void *)value) T(std::forward<Args>(args)...); + } + + void Free(T *value) { + assert(n_initialized <= n_max); + assert(n_allocated > 0); + assert(n_allocated <= n_initialized); + + Slice *slice = reinterpret_cast<Slice *>(value); + assert(slice >= data && slice < data + n_max); + + /* destruct the object */ + value->~T(); + + /* insert the slice in the "available" linked list */ + slice->next = available; + available = slice; + --n_allocated; + + /* give memory back to the kernel when the last slice + was freed */ + if (n_allocated == 0) { + HugeDiscard(data, CalcAllocationSize()); + n_initialized = 0; + available = nullptr; + } + } +}; + +#endif diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h index e44693b1d..54cb789bb 100644 --- a/src/util/bit_reverse.h +++ b/src/util/bit_reverse.h @@ -20,12 +20,13 @@ #ifndef MPD_BIT_REVERSE_H #define MPD_BIT_REVERSE_H -#include <glib.h> +#include "gcc.h" + #include <stdint.h> extern const uint8_t bit_reverse_table[256]; -G_GNUC_CONST +gcc_const static inline uint8_t bit_reverse(uint8_t x) { diff --git a/src/util/list.h b/src/util/list.h index fdab47675..28c70954f 100644 --- a/src/util/list.h +++ b/src/util/list.h @@ -47,8 +47,8 @@ * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) +#define LIST_POISON1 ((struct list_head *)(void *) 0x00100100) +#define LIST_POISON2 ((struct list_head *)(void *) 0x00200200) /* * Simple doubly linked list implementation. @@ -82,46 +82,47 @@ static inline void INIT_LIST_HEAD(struct list_head *list) * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST -static inline void __list_add(struct list_head *new, +static inline void __list_add(struct list_head *new_item, struct list_head *prev, struct list_head *next) { - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; + next->prev = new_item; + new_item->next = next; + new_item->prev = prev; + prev->next = new_item; } #else -extern void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next); +extern void __list_add(struct list_head *new_item, + struct list_head *prev, + struct list_head *next); #endif /** * list_add - add a new entry - * @new: new entry to be added + * @new_item: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static inline void list_add(struct list_head *new, struct list_head *head) +static inline void list_add(struct list_head *new_item, struct list_head *head) { - __list_add(new, head, head->next); + __list_add(new_item, head, head->next); } /** * list_add_tail - add a new entry - * @new: new entry to be added + * @new_item: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) +static inline void +list_add_tail(struct list_head *new_item, struct list_head *head) { - __list_add(new, head->prev, head); + __list_add(new_item, head->prev, head); } /* @@ -163,23 +164,23 @@ extern void list_del(struct list_head *entry); /** * list_replace - replace old entry by new one * @old : the element to be replaced - * @new : the new element to insert + * @new_item : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, - struct list_head *new) + struct list_head *new_item) { - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; + new_item->next = old->next; + new_item->next->prev = new_item; + new_item->prev = old->prev; + new_item->prev->next = new_item; } static inline void list_replace_init(struct list_head *old, - struct list_head *new) + struct list_head *new_item) { - list_replace(old, new); + list_replace(old, new_item); INIT_LIST_HEAD(old); } |