Skip to content

dix: Usage preferred allocation memory on stack#1919

Open
GermanAizek wants to merge 8 commits intoX11Libre:masterfrom
GermanAizek:stack-alloc-pool
Open

dix: Usage preferred allocation memory on stack#1919
GermanAizek wants to merge 8 commits intoX11Libre:masterfrom
GermanAizek:stack-alloc-pool

Conversation

@GermanAizek
Copy link
Copy Markdown
Contributor

@metux,
There are many changes in dix core that aim to use stack more logically for a small amount data than heap.
I also noticed a bug that grab->xi2mask is freed by usual free(), not xi2mask_free()

References:

Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
…lidate

Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
@GermanAizek
Copy link
Copy Markdown
Contributor Author

@metux,
all tests have been passed successfully, assign code reviewers.

@GermanAizek GermanAizek force-pushed the stack-alloc-pool branch 2 times, most recently from 20b38e0 to ef6e6d7 Compare January 31, 2026 13:17
Signed-off-by: Herman Semenoff <GermanAizek@yandex.ru>
@GermanAizek GermanAizek changed the title Usage preferred allocation memory on stack dix: Usage preferred allocation memory on stack Jan 31, 2026
@GermanAizek
Copy link
Copy Markdown
Contributor Author

Added new commit.
Again all tests have been passed successfully.

Comment thread dix/gc.c
}
else if (newdash != 0) {
unsigned char *dash = calloc(2, sizeof(unsigned char));
unsigned char *dash = alloca(2 * sizeof(unsigned char));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already been there myself, but had to pull back, because unfortunately alloca() isn't safe at all:

It can't detect a stack overflow (at least not portably) and could hand out invalid memory regions (outside of call stack). See: https://man7.org/linux/man-pages/man3/alloca.3.html

Something we could do is using a per-request arena allocator, which can only allocate or clear/destroy the whole arena. Then we could have one arena per client that's cleared (not destroyed) right after the request dispatch had returned - now all request handlers could just allocate their temporary space from there w/o having to care about free'ing anymore.

Here's a little PoC for this:
https://github.com/metux/libephemeron

OTOH, not sure whether it's really worth that ... we should discuss this more closely
@algrid @b-aaz @josephcrowell @cepelinas9000 @stefan11111 @X11Libre/dev

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I see it, alloca should never be used, because there is no way to detect memory allocation issues.
From the man page:

RETURN VALUE
       The alloca() function returns a pointer to the beginning of the
       allocated space.  If the allocation causes stack overflow, program
       behavior is undefined.

The way I read this, an alloca implementation is free to simply align and subtract our requested size from rsp, and give us back that address.

Also note that the stack is usually only a few MB large, and we need the stack for more than just memory allocations, so we can easily overflow it.

While this us pretty obvious, we also have to be careful to not replace some *alloc with alloca, and then return that from a function.

I haven't checked the surrounding code, but for small allocations like this, we could instead do: unsigned char mem[2]; unsigned char *dash = mem;, and clear it if we need to.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, if it’s a small fixed size, I would just declare a variable. Simple and no need to care about freeing it.
In general what’s the reasoning behind switching to alloca? Is that piece of code extremely important for performance?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Back when I attempted the same, my idea was
a) get rid of explicit free()'s (also simplify error pathes)
b) faster allocation -> alloca() just moving the stack pointer.

But I had to drop the idea, because alloca() is very fragile.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if alloca() was safe, then it would be a good thing to do.

Comment thread dix/atom.c
static unsigned long tableLength;
static NodePtr *nodeTable;

static NodePtr freeNodeRecs = NULL;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it's worth this:

  • the free()'s only happing rarely
  • atom creation is pretty rare
  • atoms are never destroyed
  • we're thinking about using a different implementation (eg generic hashtable) anyways

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants