Compare commits
32 Commits
master
...
refac/memo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f54ac456dd | ||
|
|
7782954e6e | ||
|
|
7ce62fe0cb | ||
|
|
47e6a3090a | ||
|
|
ac5b3f07d7 | ||
|
|
9df27e9e8e | ||
|
|
846ca178f5 | ||
|
|
ac0081de10 | ||
|
|
74ee6354e6 | ||
|
|
a64f83e0ed | ||
|
|
5ae42cafc4 | ||
|
|
78c92e8314 | ||
|
|
29df871800 | ||
|
|
1cd8b7a895 | ||
|
|
62606b64fe | ||
|
|
68d7157c8e | ||
|
|
e21c00e9b7 | ||
|
|
bd1ee2983b | ||
|
|
d35df8ee7b | ||
|
|
5349be6a63 | ||
|
|
71e9b45446 | ||
|
|
119efeec01 | ||
|
|
2f8e231b8c | ||
|
|
8b122d5a4c | ||
|
|
ef7b8c68d5 | ||
|
|
2cb4455a5f | ||
|
|
178888af94 | ||
|
|
3e1a96693d | ||
|
|
1cd8e15942 | ||
|
|
1340c273c7 | ||
|
|
3c73270c23 | ||
|
|
2a08909380 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,6 +25,9 @@
|
||||
[submodule "src/3rdparty/weakjack/weakjack"]
|
||||
path = src/3rdparty/weakjack/weakjack
|
||||
url = https://github.com/x42/weakjack.git
|
||||
[submodule "src/3rdparty/libcds"]
|
||||
path = src/3rdparty/libcds
|
||||
url = https://github.com/khizmax/libcds.git
|
||||
[submodule "doc/wiki"]
|
||||
path = doc/wiki
|
||||
url = https://github.com/lmms/lmms.wiki.git
|
||||
|
||||
@@ -698,6 +698,7 @@ include(CompileCache)
|
||||
ADD_SUBDIRECTORY(cmake)
|
||||
ADD_SUBDIRECTORY(src)
|
||||
ADD_SUBDIRECTORY(plugins)
|
||||
ADD_SUBDIRECTORY(benchmarks)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(data)
|
||||
ADD_SUBDIRECTORY(doc)
|
||||
|
||||
18
LICENSE.MIT.txt
Normal file
18
LICENSE.MIT.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
The MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
13
benchmarks/CMakeLists.txt
Normal file
13
benchmarks/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
SET(CMAKE_AUTOMOC ON)
|
||||
|
||||
ADD_EXECUTABLE(benchmarks
|
||||
EXCLUDE_FROM_ALL
|
||||
benchmark.cpp
|
||||
)
|
||||
|
||||
# TODO replace usages of include_directories in src/CMakeLists.txt with target_include_directories
|
||||
# and remove this line (also in tests/CMakeLists.txt)
|
||||
target_include_directories(benchmarks PRIVATE $<TARGET_PROPERTY:lmmsobjs,INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_static_libraries(benchmarks PRIVATE lmmsobjs)
|
||||
target_compile_features(benchmarks PRIVATE cxx_std_17)
|
||||
130
benchmarks/benchmark.cpp
Normal file
130
benchmarks/benchmark.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <thread>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "libcds.h"
|
||||
#include <cds/container/vyukov_mpmc_cycle_queue.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "PerfLog.h"
|
||||
|
||||
#include "LocklessList.h"
|
||||
#include "MemoryPool.h"
|
||||
|
||||
#include "NotePlayHandle.h"
|
||||
|
||||
using namespace lmms;
|
||||
|
||||
template<typename T>
|
||||
using LocklessQueue = cds::container::VyukovMPMCCycleQueue<T>;
|
||||
|
||||
template<typename Alloc>
|
||||
void benchmark_allocator(QString name, Alloc&& alloc, size_t n, size_t I)
|
||||
{
|
||||
using T = typename Alloc::value_type;
|
||||
constexpr size_t S = sizeof(T);
|
||||
|
||||
std::vector<T*> ptrs{n};
|
||||
PerfLogTimer timer(QString("Allocate: %1 x %2 x %3 bytes, %4")
|
||||
.arg(I).arg(n).arg(S).arg(name));
|
||||
|
||||
for (size_t i=0; i < I; i++)
|
||||
{
|
||||
for (size_t j=0; j < n; j++) {
|
||||
ptrs[j] = alloc.allocate(1);
|
||||
}
|
||||
for (size_t j=0; j < n; j++) {
|
||||
alloc.deallocate(ptrs[j], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Alloc>
|
||||
void benchmark_allocator_threaded(QString name, Alloc&& alloc, size_t n, size_t t)
|
||||
{
|
||||
using T = typename Alloc::value_type;
|
||||
constexpr size_t S = sizeof(T);
|
||||
|
||||
LocklessQueue<T*> ptrs{n};
|
||||
|
||||
PerfLogTimer timer(QString("Allocate multi-threaded: %1 x %2 bytes using %3 threads, %4")
|
||||
.arg(n).arg(S).arg(t).arg(name));
|
||||
|
||||
std::vector<std::thread> threads; threads.reserve(t*2);
|
||||
|
||||
std::atomic_uint_fast64_t allocated{0};
|
||||
std::atomic_uint_fast64_t deallocated{0};
|
||||
|
||||
for (size_t i=0; i < t; i++) {
|
||||
threads.emplace_back([&]() {
|
||||
while(allocated++ < n) {
|
||||
auto ptr = alloc.allocate(1);
|
||||
ptrs.push(ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (size_t i=0; i < t; i++) {
|
||||
threads.emplace_back([&]() {
|
||||
while(deallocated++ < n) {
|
||||
T* ptr;
|
||||
while (! ptrs.pop(ptr));
|
||||
alloc.deallocate(ptr, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (std::thread& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
new QCoreApplication(argc, argv);
|
||||
|
||||
using Stack = LocklessList<size_t>;
|
||||
{
|
||||
size_t n = 100 * 1000 * 1000;
|
||||
Stack stack{n};
|
||||
PerfLogTimer timer("LocklessList: Insert 100m entries, single-threaded, pre-allocated");
|
||||
for (size_t i=0; i < n; i++) {
|
||||
stack.push(i);
|
||||
}
|
||||
}
|
||||
{
|
||||
size_t n = 50 * 1000 * 1000;
|
||||
size_t t = 5;
|
||||
Stack stack{n};
|
||||
std::vector<std::thread> threads; threads.reserve(t);
|
||||
PerfLogTimer timer("LocklessList: Push 50m entries, multi-threaded, pre-allocated");
|
||||
|
||||
for (int i=0; i < 5; i++) {
|
||||
threads.emplace_back([&]() {
|
||||
for (size_t j=0; j < n / t; j++) {
|
||||
stack.push(j);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int i=0; i < 5; i++) {
|
||||
threads.at(i).join();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
size_t n = 10 * 1000 * 1000;
|
||||
constexpr size_t S = 256;
|
||||
using T = std::array<char, S>;
|
||||
benchmark_allocator("std::allocator", std::allocator<T>{}, n, 1);
|
||||
benchmark_allocator("MemoryPool", MemoryPool<T>{n}, n, 1);
|
||||
}
|
||||
{
|
||||
size_t n = 10 * 1000 * 1000;
|
||||
constexpr size_t S = 256;
|
||||
using T = std::array<char, S>;
|
||||
benchmark_allocator_threaded("std::allocator", std::allocator<T>{}, n, 4);
|
||||
benchmark_allocator_threaded("MemoryPool", MemoryPool<T>{n}, n, 4);
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,8 @@ SET(ENV{LC_ALL} "C")
|
||||
SET(ENV{LANG} "en_US")
|
||||
|
||||
# Submodule list pairs, unparsed (WARNING: Assumes alpha-numeric paths)
|
||||
STRING(REGEX MATCHALL "path = [-0-9A-Za-z/]+" SUBMODULE_LIST_RAW ${SUBMODULE_DATA})
|
||||
STRING(REGEX MATCHALL "url = [.:%-0-9A-Za-z/]+" SUBMODULE_URL_RAW ${SUBMODULE_DATA})
|
||||
STRING(REGEX MATCHALL "path = [-0-9A-Za-z/_]+" SUBMODULE_LIST_RAW ${SUBMODULE_DATA})
|
||||
STRING(REGEX MATCHALL "url = [.:%-0-9A-Za-z/_]+" SUBMODULE_URL_RAW ${SUBMODULE_DATA})
|
||||
|
||||
# Submodule list pairs, parsed
|
||||
SET(SUBMODULE_LIST "")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* BufferManager.h - A buffer caching/memory management system
|
||||
* BufferPool.h
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
@@ -23,8 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_BUFFER_MANAGER_H
|
||||
#define LMMS_BUFFER_MANAGER_H
|
||||
#pragma once
|
||||
|
||||
#include "lmms_export.h"
|
||||
#include "lmms_basics.h"
|
||||
@@ -34,7 +33,8 @@ namespace lmms
|
||||
|
||||
class SampleFrame;
|
||||
|
||||
class LMMS_EXPORT BufferManager
|
||||
/// Legacy interface for buffer re-use. Uses MemoryPool internally now.
|
||||
class LMMS_EXPORT BufferPool
|
||||
{
|
||||
public:
|
||||
static void init( fpp_t fpp );
|
||||
@@ -47,5 +47,3 @@ private:
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_BUFFER_MANAGER_H
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* LocklessAllocator.h - allocator with lockless alloc and free
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_LOCKLESS_ALLOCATOR_H
|
||||
#define LMMS_LOCKLESS_ALLOCATOR_H
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
class LocklessAllocator
|
||||
{
|
||||
public:
|
||||
LocklessAllocator( size_t nmemb, size_t size );
|
||||
virtual ~LocklessAllocator();
|
||||
void * alloc();
|
||||
void free( void * ptr );
|
||||
|
||||
|
||||
private:
|
||||
char * m_pool;
|
||||
size_t m_capacity;
|
||||
size_t m_elementSize;
|
||||
|
||||
std::atomic_int * m_freeState;
|
||||
size_t m_freeStateSets;
|
||||
|
||||
std::atomic_size_t m_available;
|
||||
std::atomic_size_t m_startIndex;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class LocklessAllocatorT : private LocklessAllocator
|
||||
{
|
||||
public:
|
||||
LocklessAllocatorT( size_t nmemb ) :
|
||||
LocklessAllocator( nmemb, sizeof( T ) )
|
||||
{
|
||||
}
|
||||
|
||||
~LocklessAllocatorT() override = default;
|
||||
|
||||
T * alloc()
|
||||
{
|
||||
return (T *)LocklessAllocator::alloc();
|
||||
}
|
||||
|
||||
void free( T * ptr )
|
||||
{
|
||||
LocklessAllocator::free( ptr );
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_LOCKLESS_ALLOCATOR_H
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef LMMS_LOCKLESS_LIST_H
|
||||
#define LMMS_LOCKLESS_LIST_H
|
||||
|
||||
#include "LocklessAllocator.h"
|
||||
#include "MemoryPool.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
LocklessList( size_t size ) :
|
||||
m_first(nullptr),
|
||||
m_allocator(new LocklessAllocatorT<Element>(size))
|
||||
m_allocator(new MemoryPool<Element>(size))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
|
||||
void push( T value )
|
||||
{
|
||||
Element * e = m_allocator->alloc();
|
||||
Element * e = m_allocator->allocate();
|
||||
e->value = value;
|
||||
e->next = m_first.load(std::memory_order_relaxed);
|
||||
|
||||
@@ -84,14 +84,13 @@ public:
|
||||
|
||||
void free( Element * e )
|
||||
{
|
||||
m_allocator->free( e );
|
||||
m_allocator->destroy( e );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::atomic<Element*> m_first;
|
||||
LocklessAllocatorT<Element> * m_allocator;
|
||||
|
||||
MemoryPool<Element> * m_allocator;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
88
include/MemoryPool.h
Normal file
88
include/MemoryPool.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* MemoryPool.h
|
||||
*
|
||||
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This file is licensed under the MIT license. See LICENSE.MIT.txt file in the
|
||||
* project root for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include "lmms_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class LMMS_EXPORT _MemoryPool_Private;
|
||||
|
||||
class LMMS_EXPORT _MemoryPool_Base
|
||||
{
|
||||
public:
|
||||
_MemoryPool_Base(size_t size, size_t nmemb);
|
||||
virtual ~_MemoryPool_Base();
|
||||
void * allocate();
|
||||
void * allocate_bounded();
|
||||
void deallocate(void * ptr);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<_MemoryPool_Private> _imp;
|
||||
};
|
||||
|
||||
/// Thread-safe, lockless memory pool. Only supports allocate(n) with n=1. When
|
||||
/// the pool is exhausted, MmAllocator is used.
|
||||
template<typename T>
|
||||
class MemoryPool : private _MemoryPool_Base
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
template<class U> struct rebind { typedef MemoryPool<U> other; };
|
||||
|
||||
MemoryPool(size_t nmemb) : _MemoryPool_Base(sizeof(T), nmemb) {}
|
||||
|
||||
T * allocate(size_t n = 1)
|
||||
{
|
||||
if (n != 1) { throw std::bad_alloc{}; }
|
||||
return reinterpret_cast<T*>(_MemoryPool_Base::allocate());
|
||||
}
|
||||
|
||||
T * allocate_bounded()
|
||||
{
|
||||
return reinterpret_cast<T*>(_MemoryPool_Base::allocate_bounded());
|
||||
}
|
||||
|
||||
void deallocate(T * ptr, size_t n = 1)
|
||||
{
|
||||
_MemoryPool_Base::deallocate(ptr);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
T * construct(Args&&... args)
|
||||
{
|
||||
T* buffer = allocate();
|
||||
return ::new ((void*)buffer) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args >
|
||||
T * construct_bounded(Args&&... args)
|
||||
{
|
||||
T* buffer = allocate_bounded();
|
||||
if (buffer) {
|
||||
::new ((void*)buffer) T(std::forward<Args>(args)...);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void destroy(T* ptr)
|
||||
{
|
||||
ptr->~T();
|
||||
deallocate(ptr);
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace lmms
|
||||
65
include/NiftyCounter.h
Normal file
65
include/NiftyCounter.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* NiftyCounter.h
|
||||
*
|
||||
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This file is licensed under the MIT license. See LICENSE.MIT.txt file in the
|
||||
* project root for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/// Nifty counter, also known as "Schwarz Counter". Used for ensuring global
|
||||
/// static object initialization and destruction order.
|
||||
|
||||
template <typename T> class _NiftyCounter_Base
|
||||
{
|
||||
public:
|
||||
_NiftyCounter_Base()
|
||||
{
|
||||
if (! T::inc()) T::init();
|
||||
}
|
||||
_NiftyCounter_Base(const _NiftyCounter_Base& other) : _NiftyCounter_Base() {}
|
||||
|
||||
~_NiftyCounter_Base()
|
||||
{
|
||||
if (! T::dec()) T::deinit();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, void C(), void D()> class _NiftyCounterCD_Base : public _NiftyCounter_Base<T>
|
||||
{
|
||||
friend class _NiftyCounter_Base<T>;
|
||||
private:
|
||||
static void init() { C(); }
|
||||
static void deinit() { D(); }
|
||||
static int inc() { return T::s_count++; }
|
||||
static int dec() { return --T::s_count; }
|
||||
};
|
||||
|
||||
|
||||
/// Pass construction and destruction functions as template arguments C and D.
|
||||
template <void C(), void D()> class NiftyCounter : public _NiftyCounterCD_Base<NiftyCounter<C,D>, C,D>
|
||||
{
|
||||
friend class _NiftyCounterCD_Base<NiftyCounter<C,D>, C,D>;
|
||||
private:
|
||||
static int s_count;
|
||||
};
|
||||
template <void C(), void D()> int NiftyCounter<C, D>::s_count = 0;
|
||||
|
||||
/// Thread-local version of NiftyCounter
|
||||
template <void C(), void D()> class NiftyCounterTL : public _NiftyCounterCD_Base<NiftyCounterTL<C,D>, C,D>
|
||||
{
|
||||
friend class _NiftyCounterCD_Base<NiftyCounterTL<C,D>, C,D>;
|
||||
private:
|
||||
thread_local static int s_count;
|
||||
};
|
||||
template <void C(), void D()> thread_local int NiftyCounterTL<C, D>::s_count = 0;
|
||||
|
||||
} // namespace lmms
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Note.h"
|
||||
#include "PlayHandle.h"
|
||||
#include "Track.h"
|
||||
#include "MemoryPool.h"
|
||||
|
||||
class QReadWriteLock;
|
||||
|
||||
@@ -332,32 +333,7 @@ private:
|
||||
bool m_frequencyNeedsUpdate; // used to update pitch
|
||||
} ;
|
||||
|
||||
|
||||
const int INITIAL_NPH_CACHE = 256;
|
||||
const int NPH_CACHE_INCREMENT = 16;
|
||||
|
||||
class NotePlayHandleManager
|
||||
{
|
||||
public:
|
||||
static void init();
|
||||
static NotePlayHandle * acquire( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t offset,
|
||||
const f_cnt_t frames,
|
||||
const Note& noteToPlay,
|
||||
NotePlayHandle* parent = nullptr,
|
||||
int midiEventChannel = -1,
|
||||
NotePlayHandle::Origin origin = NotePlayHandle::Origin::MidiClip );
|
||||
static void release( NotePlayHandle * nph );
|
||||
static void extend( int i );
|
||||
static void free();
|
||||
|
||||
private:
|
||||
static NotePlayHandle ** s_available;
|
||||
static QReadWriteLock s_mutex;
|
||||
static std::atomic_int s_availableIndex;
|
||||
static int s_size;
|
||||
};
|
||||
|
||||
extern MemoryPool<NotePlayHandle> NotePlayHandlePool;
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
18
include/libcds.h
Normal file
18
include/libcds.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "NiftyCounter.h"
|
||||
|
||||
namespace lmms::_cdslib
|
||||
{
|
||||
void init();
|
||||
void deinit();
|
||||
void thread_init();
|
||||
void thread_deinit();
|
||||
|
||||
static NiftyCounter<init, deinit> _counter;
|
||||
static thread_local NiftyCounterTL<_cdslib::thread_init, _cdslib::thread_deinit> _thread_counter;
|
||||
} // namespace lmms:_cdslib
|
||||
|
||||
#define CDS_THREAD_GUARD() (void)lmms::_cdslib::_thread_counter;
|
||||
24
src/3rdparty/CMakeLists.txt
vendored
24
src/3rdparty/CMakeLists.txt
vendored
@@ -11,6 +11,30 @@ target_include_directories(jack_headers INTERFACE jack2/common)
|
||||
ADD_SUBDIRECTORY(hiir)
|
||||
ADD_SUBDIRECTORY(weakjack)
|
||||
|
||||
ADD_LIBRARY(cds ${CDS_LIBRARY_TYPE}
|
||||
libcds/src/init.cpp
|
||||
libcds/src/hp.cpp
|
||||
libcds/src/dhp.cpp
|
||||
libcds/src/urcu_gp.cpp
|
||||
libcds/src/urcu_sh.cpp
|
||||
libcds/src/thread_data.cpp
|
||||
libcds/src/topology_hpux.cpp
|
||||
libcds/src/topology_linux.cpp
|
||||
libcds/src/topology_osx.cpp
|
||||
libcds/src/dllmain.cpp
|
||||
)
|
||||
SET_TARGET_PROPERTIES(cds PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
TARGET_INCLUDE_DIRECTORIES(cds
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libcds"
|
||||
)
|
||||
|
||||
TARGET_COMPILE_DEFINITIONS(cds
|
||||
PUBLIC CDS_BUILD_STATIC_LIB
|
||||
)
|
||||
|
||||
# The lockless ring buffer library is linked as part of the core
|
||||
add_library(ringbuffer OBJECT
|
||||
ringbuffer/src/lib/ringbuffer.cpp
|
||||
|
||||
1
src/3rdparty/libcds
vendored
Submodule
1
src/3rdparty/libcds
vendored
Submodule
Submodule src/3rdparty/libcds added at 44c052bdb6
@@ -172,7 +172,7 @@ SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS}
|
||||
target_link_libraries(lmmsobjs
|
||||
${LMMS_REQUIRED_LIBS}
|
||||
)
|
||||
target_static_libraries(lmmsobjs ringbuffer)
|
||||
target_static_libraries(lmmsobjs ringbuffer cds)
|
||||
|
||||
set_target_properties(lmms PROPERTIES
|
||||
ENABLE_EXPORTS ON
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "NotePlayHandle.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "SamplePlayHandle.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
// platform-specific audio-interface-classes
|
||||
#include "AudioAlsa.h"
|
||||
@@ -59,8 +60,6 @@
|
||||
#include "MidiApple.h"
|
||||
#include "MidiDummy.h"
|
||||
|
||||
#include "BufferManager.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
@@ -131,8 +130,8 @@ AudioEngine::AudioEngine( bool renderOnly ) :
|
||||
// allocte the FIFO from the determined size
|
||||
m_fifo = new Fifo( fifoSize );
|
||||
|
||||
// now that framesPerPeriod is fixed initialize global BufferManager
|
||||
BufferManager::init( m_framesPerPeriod );
|
||||
// now that framesPerPeriod is fixed initialize global BufferPool
|
||||
BufferPool::init( m_framesPerPeriod );
|
||||
|
||||
m_outputBufferRead = std::make_unique<SampleFrame[]>(m_framesPerPeriod);
|
||||
m_outputBufferWrite = std::make_unique<SampleFrame[]>(m_framesPerPeriod);
|
||||
@@ -329,7 +328,7 @@ void AudioEngine::renderStageNoteSetup()
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandlePool.destroy( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
m_playHandles.erase( it );
|
||||
@@ -392,7 +391,7 @@ void AudioEngine::renderStageEffects()
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandlePool.destroy( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
@@ -610,7 +609,7 @@ bool AudioEngine::addPlayHandle( PlayHandle* handle )
|
||||
|
||||
if( handle->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*)handle );
|
||||
NotePlayHandlePool.destroy( (NotePlayHandle*)handle );
|
||||
}
|
||||
else delete handle;
|
||||
|
||||
@@ -661,7 +660,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph)
|
||||
{
|
||||
if (ph->type() == PlayHandle::Type::NotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release(dynamic_cast<NotePlayHandle*>(ph));
|
||||
NotePlayHandlePool.destroy(dynamic_cast<NotePlayHandle*>(ph));
|
||||
}
|
||||
else { delete ph; }
|
||||
}
|
||||
@@ -687,7 +686,7 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types type
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandlePool.destroy( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* BufferManager.cpp - A buffer caching/memory management system
|
||||
* BufferPool.cpp
|
||||
*
|
||||
* Copyright (c) 2017 Lukas W <lukaswhl/at/gmail.com>
|
||||
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
@@ -24,34 +24,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BufferManager.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
#include "SampleFrame.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "MemoryPool.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
fpp_t BufferManager::s_framesPerPeriod;
|
||||
static std::unique_ptr<_MemoryPool_Base> pool;
|
||||
const int BM_INITIAL_BUFFERS = 256;
|
||||
|
||||
void BufferManager::init( fpp_t fpp )
|
||||
void BufferPool::init( fpp_t framesPerPeriod )
|
||||
{
|
||||
s_framesPerPeriod = fpp;
|
||||
pool.reset(new _MemoryPool_Base(framesPerPeriod * sizeof(SampleFrame), BM_INITIAL_BUFFERS));
|
||||
}
|
||||
|
||||
|
||||
SampleFrame* BufferManager::acquire()
|
||||
SampleFrame * BufferPool::acquire()
|
||||
{
|
||||
return new SampleFrame[s_framesPerPeriod];
|
||||
return reinterpret_cast<SampleFrame*>(pool->allocate());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BufferManager::release( SampleFrame* buf )
|
||||
void BufferPool::release( SampleFrame * buf )
|
||||
{
|
||||
delete[] buf;
|
||||
pool->deallocate(buf);
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -10,7 +10,7 @@ set(LMMS_SRCS
|
||||
core/AutomationNode.cpp
|
||||
core/BandLimitedWave.cpp
|
||||
core/base64.cpp
|
||||
core/BufferManager.cpp
|
||||
core/BufferPool.cpp
|
||||
core/Clipboard.cpp
|
||||
core/ComboBoxModel.cpp
|
||||
core/ConfigManager.cpp
|
||||
@@ -38,7 +38,7 @@ set(LMMS_SRCS
|
||||
core/LadspaManager.cpp
|
||||
core/LfoController.cpp
|
||||
core/LinkedModelGroups.cpp
|
||||
core/LocklessAllocator.cpp
|
||||
core/MemoryPool.cpp
|
||||
core/MeterModel.cpp
|
||||
core/Metronome.cpp
|
||||
core/MicroTimer.cpp
|
||||
@@ -132,5 +132,7 @@ set(LMMS_SRCS
|
||||
core/midi/MidiPort.cpp
|
||||
core/midi/MidiWinMM.cpp
|
||||
|
||||
core/libcds.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -265,7 +265,7 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
// create sub-note-play-handle, only note is
|
||||
// different
|
||||
Engine::audioEngine()->addPlayHandle(
|
||||
NotePlayHandleManager::acquire( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
NotePlayHandlePool.construct( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
_n, -1, NotePlayHandle::Origin::NoteStacking )
|
||||
);
|
||||
}
|
||||
@@ -513,7 +513,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// create sub-note-play-handle, only ptr to note is different
|
||||
// and is_arp_note=true
|
||||
Engine::audioEngine()->addPlayHandle(
|
||||
NotePlayHandleManager::acquire( _n->instrumentTrack(),
|
||||
NotePlayHandlePool.construct( _n->instrumentTrack(),
|
||||
frames_processed,
|
||||
gated_frames,
|
||||
Note( TimePos( 0 ), TimePos( 0 ), sub_note_key, _n->getVolume(),
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* LocklessAllocator.cpp - allocator with lockless alloc and free
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LocklessAllocator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifndef LMMS_BUILD_WIN32
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
static const size_t SIZEOF_SET = sizeof( int ) * 8;
|
||||
|
||||
|
||||
static size_t align( size_t size, size_t alignment )
|
||||
{
|
||||
size_t misalignment = size % alignment;
|
||||
if( misalignment )
|
||||
{
|
||||
size += alignment - misalignment;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size )
|
||||
{
|
||||
m_capacity = align( nmemb, SIZEOF_SET );
|
||||
m_elementSize = align( size, sizeof( void * ) );
|
||||
m_pool = new char[m_capacity * m_elementSize];
|
||||
|
||||
m_freeStateSets = m_capacity / SIZEOF_SET;
|
||||
m_freeState = new std::atomic_int[m_freeStateSets];
|
||||
std::fill(m_freeState, m_freeState + m_freeStateSets, 0);
|
||||
|
||||
m_available = m_capacity;
|
||||
m_startIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LocklessAllocator::~LocklessAllocator()
|
||||
{
|
||||
if (m_available != m_capacity)
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: "
|
||||
"Destroying with elements still allocated\n" );
|
||||
}
|
||||
|
||||
delete[] m_pool;
|
||||
delete[] m_freeState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
static int ffs( int i )
|
||||
{
|
||||
if( !i )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for( int j = 0;; )
|
||||
{
|
||||
if( i & 1 << j++ )
|
||||
{
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
void * LocklessAllocator::alloc()
|
||||
{
|
||||
// Some of these CAS loops could probably use relaxed atomics, as discussed
|
||||
// in http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange.
|
||||
// Let's use sequentially-consistent ops to be safe for now.
|
||||
auto available = m_available.load();
|
||||
do
|
||||
{
|
||||
if( !available )
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: No free space\n" );
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
while (!m_available.compare_exchange_weak(available, available - 1));
|
||||
|
||||
const size_t startIndex = m_startIndex++ % m_freeStateSets;
|
||||
for (size_t set = startIndex;; set = ( set + 1 ) % m_freeStateSets)
|
||||
{
|
||||
for (int freeState = m_freeState[set]; freeState != -1;)
|
||||
{
|
||||
int bit = ffs( ~freeState ) - 1;
|
||||
if (m_freeState[set].compare_exchange_weak(freeState,
|
||||
freeState | 1 << bit ) )
|
||||
{
|
||||
return m_pool + ( SIZEOF_SET * set + bit )
|
||||
* m_elementSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LocklessAllocator::free( void * ptr )
|
||||
{
|
||||
ptrdiff_t diff = (char *)ptr - m_pool;
|
||||
if( diff < 0 || diff % m_elementSize )
|
||||
{
|
||||
invalid:
|
||||
fprintf( stderr, "LocklessAllocator: Invalid pointer\n" );
|
||||
return;
|
||||
}
|
||||
size_t offset = diff / m_elementSize;
|
||||
if( offset >= m_capacity )
|
||||
{
|
||||
goto invalid;
|
||||
}
|
||||
size_t set = offset / SIZEOF_SET;
|
||||
int bit = offset % SIZEOF_SET;
|
||||
int mask = 1 << bit;
|
||||
int prevState = m_freeState[set].fetch_and(~mask);
|
||||
if ( !( prevState & mask ) )
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: Block not in use\n" );
|
||||
return;
|
||||
}
|
||||
++m_available;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
125
src/core/MemoryPool.cpp
Normal file
125
src/core/MemoryPool.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* MemoryPool.cpp
|
||||
*
|
||||
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This file is licensed under the MIT license. See LICENSE.MIT.txt file in the
|
||||
* project root for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "MemoryPool.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "libcds.h"
|
||||
#include <cds/container/vyukov_mpmc_cycle_queue.h>
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class _MemoryPool_Private
|
||||
{
|
||||
public:
|
||||
_MemoryPool_Private(size_t size, size_t nmemb)
|
||||
: m_elementSize(size)
|
||||
, m_numElms(nmemb)
|
||||
, m_freelist(nmemb)
|
||||
{
|
||||
CDS_THREAD_GUARD();
|
||||
|
||||
m_buffer = new char[m_elementSize * m_numElms];
|
||||
for (size_t i = 0; i < m_numElms; i++) {
|
||||
m_freelist.push(m_buffer + (i * m_elementSize));
|
||||
}
|
||||
}
|
||||
|
||||
~_MemoryPool_Private()
|
||||
{
|
||||
char* ptr = nullptr;
|
||||
while (m_freelist.pop(ptr)) {
|
||||
if (! is_from_pool(ptr)) {
|
||||
delete[] ptr;
|
||||
}
|
||||
}
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
void * allocate()
|
||||
{
|
||||
void* ptr = allocate_bounded();
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
} else {
|
||||
qWarning() << "MemoryPool exhausted";
|
||||
return new char[m_elementSize];
|
||||
}
|
||||
}
|
||||
|
||||
void * allocate_bounded()
|
||||
{
|
||||
char* ptr = nullptr;
|
||||
m_freelist.pop(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(void * ptr)
|
||||
{
|
||||
if (is_from_pool(ptr)) {
|
||||
bool pushed = m_freelist.push(reinterpret_cast<char*>(ptr));
|
||||
assert(pushed); Q_UNUSED(pushed);
|
||||
} else {
|
||||
do_deallocate(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void* do_allocate()
|
||||
{
|
||||
return new char[m_elementSize];
|
||||
}
|
||||
void do_deallocate(void* ptr)
|
||||
{
|
||||
delete[] reinterpret_cast<char*>(ptr);
|
||||
}
|
||||
|
||||
bool is_from_pool(void* ptr)
|
||||
{
|
||||
auto buff = reinterpret_cast<uintptr_t>(m_buffer);
|
||||
auto p = reinterpret_cast<uintptr_t>(ptr);
|
||||
return p >= buff && p < (buff + (m_elementSize * m_numElms));
|
||||
}
|
||||
|
||||
const size_t m_elementSize;
|
||||
const size_t m_numElms;
|
||||
|
||||
char* m_buffer;
|
||||
cds::container::VyukovMPMCCycleQueue<char*> m_freelist;
|
||||
};
|
||||
|
||||
_MemoryPool_Base::_MemoryPool_Base( size_t size, size_t nmemb )
|
||||
: _imp(new _MemoryPool_Private(size, nmemb))
|
||||
{}
|
||||
|
||||
_MemoryPool_Base::~_MemoryPool_Base()
|
||||
{}
|
||||
|
||||
void * _MemoryPool_Base::allocate()
|
||||
{
|
||||
return _imp->allocate();
|
||||
}
|
||||
|
||||
void *_MemoryPool_Base::allocate_bounded()
|
||||
{
|
||||
return _imp->allocate_bounded();
|
||||
}
|
||||
|
||||
void _MemoryPool_Base::deallocate(void * ptr)
|
||||
{
|
||||
return _imp->deallocate(ptr);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "AudioEngine.h"
|
||||
#include "AudioEngineWorkerThread.h"
|
||||
#include "BufferManager.h"
|
||||
#include "BufferPool.h"
|
||||
#include "Mixer.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "Song.h"
|
||||
|
||||
@@ -601,77 +601,8 @@ void NotePlayHandle::resize( const bpm_t _new_tempo )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NotePlayHandle ** NotePlayHandleManager::s_available;
|
||||
QReadWriteLock NotePlayHandleManager::s_mutex;
|
||||
std::atomic_int NotePlayHandleManager::s_availableIndex;
|
||||
int NotePlayHandleManager::s_size;
|
||||
|
||||
|
||||
void NotePlayHandleManager::init()
|
||||
{
|
||||
s_available = new NotePlayHandle*[INITIAL_NPH_CACHE];
|
||||
|
||||
auto n = static_cast<NotePlayHandle *>(std::malloc(sizeof(NotePlayHandle) * INITIAL_NPH_CACHE));
|
||||
|
||||
for( int i=0; i < INITIAL_NPH_CACHE; ++i )
|
||||
{
|
||||
s_available[ i ] = n;
|
||||
++n;
|
||||
}
|
||||
s_availableIndex = INITIAL_NPH_CACHE - 1;
|
||||
s_size = INITIAL_NPH_CACHE;
|
||||
}
|
||||
|
||||
|
||||
NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t offset,
|
||||
const f_cnt_t frames,
|
||||
const Note& noteToPlay,
|
||||
NotePlayHandle* parent,
|
||||
int midiEventChannel,
|
||||
NotePlayHandle::Origin origin )
|
||||
{
|
||||
// TODO: use some lockless data structures
|
||||
s_mutex.lockForWrite();
|
||||
if (s_availableIndex < 0) { extend(NPH_CACHE_INCREMENT); }
|
||||
NotePlayHandle * nph = s_available[s_availableIndex--];
|
||||
s_mutex.unlock();
|
||||
|
||||
new( (void*)nph ) NotePlayHandle( instrumentTrack, offset, frames, noteToPlay, parent, midiEventChannel, origin );
|
||||
return nph;
|
||||
}
|
||||
|
||||
|
||||
void NotePlayHandleManager::release( NotePlayHandle * nph )
|
||||
{
|
||||
nph->NotePlayHandle::~NotePlayHandle();
|
||||
s_mutex.lockForRead();
|
||||
s_available[++s_availableIndex] = nph;
|
||||
s_mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
void NotePlayHandleManager::extend( int c )
|
||||
{
|
||||
s_size += c;
|
||||
auto tmp = new NotePlayHandle*[s_size];
|
||||
delete[] s_available;
|
||||
s_available = tmp;
|
||||
|
||||
auto n = static_cast<NotePlayHandle *>(std::malloc(sizeof(NotePlayHandle) * c));
|
||||
|
||||
for( int i=0; i < c; ++i )
|
||||
{
|
||||
s_available[++s_availableIndex] = n;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
void NotePlayHandleManager::free()
|
||||
{
|
||||
delete[] s_available;
|
||||
}
|
||||
const size_t INITIAL_NPH_CACHE = 256;
|
||||
MemoryPool<NotePlayHandle> NotePlayHandlePool{INITIAL_NPH_CACHE};
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
#include "BufferManager.h"
|
||||
#include "Engine.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "AutomatableModel.h"
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include "PlayHandle.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "BufferManager.h"
|
||||
#include "Engine.h"
|
||||
#include "AudioEngine.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
@@ -37,7 +37,7 @@ PlayHandle::PlayHandle(const Type type, f_cnt_t offset) :
|
||||
m_type(type),
|
||||
m_offset(offset),
|
||||
m_affinity(QThread::currentThread()),
|
||||
m_playHandleBuffer(BufferManager::acquire()),
|
||||
m_playHandleBuffer(BufferPool::acquire()),
|
||||
m_bufferReleased(true),
|
||||
m_usesBuffer(true)
|
||||
{
|
||||
@@ -46,7 +46,7 @@ PlayHandle::PlayHandle(const Type type, f_cnt_t offset) :
|
||||
|
||||
PlayHandle::~PlayHandle()
|
||||
{
|
||||
BufferManager::release(m_playHandleBuffer);
|
||||
BufferPool::release(m_playHandleBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
|
||||
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
// create note-play-handle for it
|
||||
m_previewNote = NotePlayHandleManager::acquire(
|
||||
m_previewNote = NotePlayHandlePool.construct(
|
||||
s_previewTC->previewInstrumentTrack(), 0,
|
||||
std::numeric_limits<f_cnt_t>::max() / 2,
|
||||
Note( 0, 0, DefaultKey, 100 ) );
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "BufferManager.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "Mixer.h"
|
||||
#include "Engine.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "BufferManager.h"
|
||||
#include "BufferPool.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
@@ -38,7 +38,7 @@ AudioPort::AudioPort( const QString & _name, bool _has_effect_chain,
|
||||
FloatModel * volumeModel, FloatModel * panningModel,
|
||||
BoolModel * mutedModel ) :
|
||||
m_bufferUsage( false ),
|
||||
m_portBuffer( BufferManager::acquire() ),
|
||||
m_portBuffer( BufferPool::acquire() ),
|
||||
m_extOutputEnabled( false ),
|
||||
m_nextMixerChannel( 0 ),
|
||||
m_name( "unnamed port" ),
|
||||
@@ -58,7 +58,7 @@ AudioPort::~AudioPort()
|
||||
{
|
||||
setExtOutputEnabled( false );
|
||||
Engine::audioEngine()->removeAudioPort( this );
|
||||
BufferManager::release( m_portBuffer );
|
||||
BufferPool::release( m_portBuffer );
|
||||
}
|
||||
|
||||
|
||||
|
||||
34
src/core/libcds.cpp
Normal file
34
src/core/libcds.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "libcds.h"
|
||||
|
||||
#include <cds/init.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace lmms::_cdslib
|
||||
{
|
||||
|
||||
void init()
|
||||
{
|
||||
cds::Initialize();
|
||||
}
|
||||
|
||||
void deinit()
|
||||
{
|
||||
cds::Terminate();
|
||||
}
|
||||
|
||||
void thread_init()
|
||||
{
|
||||
if (! cds::threading::Manager::isThreadAttached()) {
|
||||
cds::threading::Manager::attachThread();
|
||||
}
|
||||
}
|
||||
|
||||
void thread_deinit()
|
||||
{
|
||||
if (cds::threading::Manager::isThreadAttached()) {
|
||||
cds::threading::Manager::detachThread();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lmms::_cdslib
|
||||
@@ -353,9 +353,6 @@ int main( int argc, char * * argv )
|
||||
}
|
||||
#endif
|
||||
|
||||
// initialize memory managers
|
||||
NotePlayHandleManager::init();
|
||||
|
||||
// intialize RNG
|
||||
srand( getpid() + time( 0 ) );
|
||||
|
||||
@@ -984,8 +981,5 @@ int main( int argc, char * * argv )
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
NotePlayHandleManager::free();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
#include "embed.h"
|
||||
#include "BufferManager.h"
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
@@ -340,7 +340,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim
|
||||
if (m_notes[event.key()] == nullptr && event.key() >= firstKey() && event.key() <= lastKey())
|
||||
{
|
||||
NotePlayHandle* nph =
|
||||
NotePlayHandleManager::acquire(
|
||||
NotePlayHandlePool.construct(
|
||||
this, offset,
|
||||
std::numeric_limits<f_cnt_t>::max() / 2,
|
||||
Note(TimePos(), Engine::getSong()->getPlayPos(Engine::getSong()->playMode()),
|
||||
@@ -781,7 +781,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
? 0
|
||||
: currentNote->length().frames(frames_per_tick);
|
||||
|
||||
NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire(this, _offset, noteFrames, *currentNote);
|
||||
NotePlayHandle* notePlayHandle = NotePlayHandlePool.construct(this, _offset, noteFrames, *currentNote);
|
||||
notePlayHandle->setPatternTrack(pattern_track);
|
||||
// are we playing global song?
|
||||
if( _clip_num < 0 )
|
||||
|
||||
@@ -9,6 +9,8 @@ set(LMMS_TESTS
|
||||
src/core/MathTest.cpp
|
||||
src/core/ProjectVersionTest.cpp
|
||||
src/core/RelativePathsTest.cpp
|
||||
src/core/MemoryPoolTest.cpp
|
||||
|
||||
src/tracks/AutomationTrackTest.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ if not Path('.gitmodules').is_file():
|
||||
print('You need to call this script from the LMMS top directory')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['git', 'ls-files', '*.[ch]', '*.[ch]pp', ':!tests/*'],
|
||||
result = subprocess.run(['git', 'ls-files', '*.[ch]', '*.[ch]pp', ':!tests/*', ':!benchmarks/*'],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
known_no_namespace_lmms = {
|
||||
|
||||
62
tests/src/core/MemoryPoolTest.cpp
Normal file
62
tests/src/core/MemoryPoolTest.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MemoryPoolTest.cpp
|
||||
*
|
||||
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "MemoryPool.h"
|
||||
|
||||
#include <array>
|
||||
#include <stack>
|
||||
|
||||
using namespace lmms;
|
||||
|
||||
class MemoryPoolTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void MemoryPoolTests()
|
||||
{
|
||||
using T = std::array<char, 16>;
|
||||
int n = 256;
|
||||
MemoryPool<T> pool(n);
|
||||
|
||||
std::stack<T*> ptrs;
|
||||
|
||||
for (int i=0; i < n; i++) {
|
||||
ptrs.push(pool.allocate_bounded());
|
||||
QVERIFY(ptrs.top());
|
||||
}
|
||||
QCOMPARE(pool.allocate_bounded(), static_cast<T*>(nullptr));
|
||||
ptrs.push(pool.allocate());
|
||||
QVERIFY(ptrs.top());
|
||||
|
||||
while (!ptrs.empty()) {
|
||||
pool.deallocate(ptrs.top());
|
||||
ptrs.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(MemoryPoolTest)
|
||||
|
||||
#include "MemoryPoolTest.moc"
|
||||
Reference in New Issue
Block a user