Skip to content

Commit 8506279

Browse files
committed
Lazily allocate and eagerly free unpacker stack
Applications may have numerous unpacker instances allocated with different configurations, but are very unlikely to have more than one or a handful active concurrently. As such eagerly allocating the 4kiB parsing stack is using more memory than necessary. Additionally, when only a few arena pages are in use, allocating a chunk from a first page is very cheap. The only complexity in this patch is the `Unpacker#each` API that requires not to free the stack on EOF as parsing may be resumed afterwards.
1 parent 2a92ffb commit 8506279

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

ext/msgpack/unpacker.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,29 @@ void msgpack_unpacker_static_destroy(void)
7979

8080
#define HEAD_BYTE_REQUIRED 0xc1
8181

82-
static inline void _msgpack_unpacker_stack_init(msgpack_unpacker_stack_t *stack) {
83-
stack->capacity = MSGPACK_UNPACKER_STACK_CAPACITY;
84-
stack->data = msgpack_rmem_alloc(&s_stack_rmem);
82+
static inline bool _msgpack_unpacker_stack_init(msgpack_unpacker_stack_t *stack) {
83+
if (!stack->data) {
84+
stack->capacity = MSGPACK_UNPACKER_STACK_CAPACITY;
85+
stack->data = msgpack_rmem_alloc(&s_stack_rmem);
86+
stack->depth = 0;
87+
return true;
88+
}
89+
return false;
8590
}
8691

92+
static inline void _msgpack_unpacker_free_stack(msgpack_unpacker_stack_t* stack) {
93+
if (stack->data) {
94+
if (!msgpack_rmem_free(&s_stack_rmem, stack->data)) {
95+
rb_bug("Failed to free an rmem pointer, memory leak?");
96+
}
97+
stack->data = NULL;
98+
stack->depth = 0;
99+
}
100+
}
101+
102+
#define STACK_INIT(uk) bool stack_allocated = _msgpack_unpacker_stack_init(&uk->stack);
103+
#define STACK_FREE(uk) if (stack_allocated) { _msgpack_unpacker_free_stack(&uk->stack); }
104+
87105
void _msgpack_unpacker_init(msgpack_unpacker_t* uk)
88106
{
89107
msgpack_buffer_init(UNPACKER_BUFFER_(uk));
@@ -92,16 +110,6 @@ void _msgpack_unpacker_init(msgpack_unpacker_t* uk)
92110

93111
uk->last_object = Qnil;
94112
uk->reading_raw = Qnil;
95-
96-
_msgpack_unpacker_stack_init(&uk->stack);
97-
}
98-
99-
static inline void _msgpack_unpacker_free_stack(msgpack_unpacker_stack_t* stack) {
100-
if (!msgpack_rmem_free(&s_stack_rmem, stack->data)) {
101-
rb_bug("Failed to free an rmem pointer, memory leak?");
102-
}
103-
stack->data = NULL;
104-
stack->depth = 0;
105113
}
106114

107115
void _msgpack_unpacker_destroy(msgpack_unpacker_t* uk)
@@ -750,9 +758,15 @@ int msgpack_unpacker_read_map_header(msgpack_unpacker_t* uk, uint32_t* result_si
750758

751759
int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
752760
{
761+
STACK_INIT(uk);
762+
753763
while(true) {
754764
int r = read_primitive(uk);
755765
if(r < 0) {
766+
if (r != PRIMITIVE_EOF) {
767+
// We keep the stack on EOF as the parsing may be resumed.
768+
STACK_FREE(uk);
769+
}
756770
return r;
757771
}
758772
if(r == PRIMITIVE_CONTAINER_START) {
@@ -761,6 +775,7 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
761775
/* PRIMITIVE_OBJECT_COMPLETE */
762776

763777
if(msgpack_unpacker_stack_is_empty(uk)) {
778+
STACK_FREE(uk);
764779
return PRIMITIVE_OBJECT_COMPLETE;
765780
}
766781

@@ -785,13 +800,15 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
785800
top->type = STACK_TYPE_MAP_KEY;
786801
break;
787802
case STACK_TYPE_RECURSIVE:
803+
STACK_FREE(uk);
788804
return PRIMITIVE_OBJECT_COMPLETE;
789805
}
790806
size_t count = --top->count;
791807

792808
if(count == 0) {
793809
object_complete(uk, top->object);
794810
if(msgpack_unpacker_stack_pop(uk) <= target_stack_depth) {
811+
STACK_FREE(uk);
795812
return PRIMITIVE_OBJECT_COMPLETE;
796813
}
797814
goto container_completed;
@@ -802,9 +819,12 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
802819

803820
int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth)
804821
{
822+
STACK_INIT(uk);
823+
805824
while(true) {
806825
int r = read_primitive(uk);
807826
if(r < 0) {
827+
STACK_FREE(uk);
808828
return r;
809829
}
810830
if(r == PRIMITIVE_CONTAINER_START) {
@@ -813,6 +833,7 @@ int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth)
813833
/* PRIMITIVE_OBJECT_COMPLETE */
814834

815835
if(msgpack_unpacker_stack_is_empty(uk)) {
836+
STACK_FREE(uk);
816837
return PRIMITIVE_OBJECT_COMPLETE;
817838
}
818839

@@ -828,6 +849,7 @@ int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth)
828849
if(count == 0) {
829850
object_complete(uk, Qnil);
830851
if(msgpack_unpacker_stack_pop(uk) <= target_stack_depth) {
852+
STACK_FREE(uk);
831853
return PRIMITIVE_OBJECT_COMPLETE;
832854
}
833855
goto container_completed;

0 commit comments

Comments
 (0)