Add memory allocation API with templated C++ delegates & minor fixups #1123

Closed
KitsuneAlex wants to merge 5 commits from main into main
3 changed files with 85 additions and 15 deletions

View File

@@ -5,6 +5,7 @@
cmake_minimum_required(VERSION 3.4.1)
project(yoga)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
@@ -13,12 +14,13 @@ add_compile_options(
-fvisibility=hidden
-ffunction-sections
-fdata-sections
-fPIC
-Wall
-std=c++11)
file(GLOB_RECURSE yogacore_SRC yoga/*.cpp)
add_library(yogacore STATIC ${yogacore_SRC})
file(GLOB_RECURSE YOGA_SRC yoga/*.cpp)
add_library(${CMAKE_PROJECT_NAME} STATIC ${YOGA_SRC})
target_include_directories(yogacore PUBLIC .)
target_link_libraries(yogacore android log)
set_target_properties(yogacore PROPERTIES CXX_STANDARD 11)
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC .)
target_link_libraries(${CMAKE_PROJECT_NAME} android log)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CXX_STANDARD 11)

View File

@@ -11,7 +11,6 @@
#include <string.h>
#include <algorithm>
#include <atomic>
#include <memory>
#include "Utils.h"
#include "YGNode.h"
#include "YGNodePrint.h"
@@ -106,6 +105,51 @@ static int YGDefaultLog(
#undef YG_UNUSED
#endif
#ifdef _MSC_VER
#define YG_ALLOC_ALIGNED ([](size_t a, size_t s) -> void* { \
return _aligned_malloc(s, a); \
})
#define YG_FREE_ALIGNED ([](void* m) -> void { \
_aligned_free(m); \
})
#else
#define YG_ALLOC_ALIGNED ([](size_t a, size_t s) -> void* { \
return aligned_alloc(a, s); \
NickGerleman commented 2022-10-03 09:20:49 -07:00 (Migrated from github.com)
Review

nit: why are these macros vs a regular function?

nit: why are these macros vs a regular function?
})
#define YG_FREE_ALIGNED ([](void* m) -> void { \
free(m); \
})
#endif
static YGAllocatorAllocateFunc gAllocatorAllocateFunc = YG_ALLOC_ALIGNED;
static YGAllocatorFreeFunc gAllocatorFreeFunc = YG_FREE_ALIGNED;
YOGA_EXPORT void YGSetAllocationCallbacks(
YGAllocatorAllocateFunc allocFunc,
YGAllocatorFreeFunc freeFunc) {
gAllocatorAllocateFunc = allocFunc;
gAllocatorFreeFunc = freeFunc;
}
NickGerleman commented 2022-10-03 09:19:44 -07:00 (Migrated from github.com)
Review

If these are global, they will need to be protected by mutex, in case multiple threads are using Yoga at once.

I think it would be cleaner if the allocator was set as part of YGConfig, though it would mean YGConfig itself could not be allocated using the custom allocator. Though, Yoga may otherwise call functions which perform their dynamic allocations, so if the goal is just for the large structures, I think it could still be okay.

If these are global, they will need to be protected by mutex, in case multiple threads are using Yoga at once. I think it would be cleaner if the allocator was set as part of YGConfig, though it would mean YGConfig itself could not be allocated using the custom allocator. Though, Yoga may otherwise call functions which perform their dynamic allocations, so if the goal is just for the large structures, I think it could still be okay.
YOGA_EXPORT void YGGetAllocationCallbacks(
YGAllocatorAllocateFunc* allocFunc,
NickGerleman commented 2022-10-03 09:25:11 -07:00 (Migrated from github.com)
Review
YOGA_EXPORT void YGSetAllocator(
```suggestion YOGA_EXPORT void YGSetAllocator( ```
YGAllocatorFreeFunc* freeFunc) {
if(allocFunc != nullptr) {
*allocFunc = gAllocatorAllocateFunc;
}
if(freeFunc != nullptr) {
*freeFunc = gAllocatorFreeFunc;
}
}
YOGA_EXPORT void* YGMemoryAllocate(size_t alignment, size_t size) {
return gAllocatorAllocateFunc(alignment, size);
}
YOGA_EXPORT void YGMemoryFree(void* memory) {
gAllocatorFreeFunc(memory);
}
static inline bool YGDoubleIsUndefined(const double value) {
NickGerleman commented 2022-10-03 09:23:45 -07:00 (Migrated from github.com)
Review

nit: Do we need to expose allocate and free as part of the public Yoga API? This already has an API for getting the allocators which are set. It doesn't seem like a good code pattern for folks to use Yoga's APIs for their own memory allocation.

nit: Do we need to expose allocate and free as part of the public Yoga API? This already has an API for getting the allocators which are set. It doesn't seem like a good code pattern for folks to use Yoga's APIs for their own memory allocation.
return facebook::yoga::isUndefined(value);
}
@@ -192,7 +236,7 @@ YOGA_EXPORT void YGNodeMarkDirtyAndPropogateToDescendants(
int32_t gConfigInstanceCount = 0;
YOGA_EXPORT WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
const YGNodeRef node = new YGNode{config};
const YGNodeRef node = YGAllocate<YGNode>(config);
YGAssertWithConfig(
config, node != nullptr, "Could not allocate memory for node");
Event::publish<Event::NodeAllocation>(node, {config});
@@ -210,7 +254,7 @@ YOGA_EXPORT YGNodeRef YGNodeNew(void) {
}
YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNode) {
YGNodeRef node = new YGNode(*oldNode);
YGNodeRef node = YGAllocate<YGNode>(*oldNode);
YGAssertWithConfig(
oldNode->getConfig(),
node != nullptr,
@@ -221,7 +265,7 @@ YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNode) {
}
static YGConfigRef YGConfigClone(const YGConfig& oldConfig) {
const YGConfigRef config = new YGConfig(oldConfig);
const YGConfigRef config = YGAllocate<YGConfig>(oldConfig);
YGAssert(config != nullptr, "Could not allocate memory for config");
gConfigInstanceCount++;
return config;
@@ -229,7 +273,7 @@ static YGConfigRef YGConfigClone(const YGConfig& oldConfig) {
static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) {
auto config = YGConfigClone(*oldNode->getConfig());
auto node = new YGNode{*oldNode, config};
auto node = YGAllocate<YGNode>(*oldNode, config);
node->setOwner(nullptr);
Event::publish<Event::NodeAllocation>(node, {node->getConfig()});
@@ -260,13 +304,13 @@ YOGA_EXPORT void YGNodeFree(const YGNodeRef node) {
node->clearChildren();
Event::publish<Event::NodeDeallocation>(node, {node->getConfig()});
delete node;
YGFree(node);
}
static void YGConfigFreeRecursive(const YGNodeRef root) {
if (root->getConfig() != nullptr) {
gConfigInstanceCount--;
delete root->getConfig();
YGFree(root->getConfig());
}
// Delete configs recursively for childrens
for (auto* child : root->getChildren()) {
@@ -308,16 +352,16 @@ int32_t YGConfigGetInstanceCount(void) {
YOGA_EXPORT YGConfigRef YGConfigNew(void) {
#ifdef ANDROID
const YGConfigRef config = new YGConfig(YGAndroidLog);
const YGConfigRef config = YGAllocate<YGConfig>(YGAndroidLog);
#else
const YGConfigRef config = new YGConfig(YGDefaultLog);
const YGConfigRef config = YGAllocate<YGConfig>(YGDefaultLog);
#endif
gConfigInstanceCount++;
return config;
}
YOGA_EXPORT void YGConfigFree(const YGConfigRef config) {
delete config;
YGFree(config);
gConfigInstanceCount--;
}

View File

@@ -29,6 +29,9 @@ typedef struct YGSize {
float height;
} YGSize;
typedef void* (*YGAllocatorAllocateFunc)(size_t alignment, size_t size);
typedef void (*YGAllocatorFreeFunc)(void* memory);
typedef struct YGConfig* YGConfigRef;
typedef struct YGNode* YGNodeRef;
@@ -53,6 +56,13 @@ typedef int (*YGLogger)(
typedef YGNodeRef (
*YGCloneNodeFunc)(YGNodeRef oldNode, YGNodeRef owner, int childIndex);
// Memory allocation
WIN_EXPORT void YGSetAllocationCallbacks(YGAllocatorAllocateFunc allocFunc, YGAllocatorFreeFunc freeFunc);
WIN_EXPORT void YGGetAllocationCallbacks(YGAllocatorAllocateFunc* allocFunc, YGAllocatorFreeFunc* freeFunc);
WIN_EXPORT void* YGMemoryAllocate(size_t alignment, size_t size);
WIN_EXPORT void YGMemoryFree(void* memory);
// YGNode
WIN_EXPORT YGNodeRef YGNodeNew(void);
WIN_EXPORT YGNodeRef YGNodeNewWithConfig(YGConfigRef config);
@@ -364,6 +374,20 @@ YG_EXTERN_C_END
#include <functional>
#include <vector>
// Templated delegates for YGMemoryAllocate & YGMemoryFree, so we don't have to
// cast nor pass in the size of the allocated chunk of memory explicitly.
template<typename T, typename... A>
T* YGAllocate(A&&... arguments) {
auto* memory = reinterpret_cast<T*>(YGMemoryAllocate(sizeof(T), alignof(T)));
new(memory) T(std::forward<A>(arguments)...);
return memory;
}
template<typename T>
void YGFree(T* memory) {
memory->~T();
YGMemoryFree(memory);
}
// Calls f on each node in the tree including the given node argument.
void YGTraversePreOrder(
YGNodeRef node,