Recent activity

Re: Stack head using the clone3 system call 10 days ago

From Christopher Wellons to ~skeeto/public-inbox

You've almost got it. You just need to fix three mistakes in two lines of 
newthread. The first two you can reason about by dimensional analysis.

1. "count" should be the number of stack_head objects that fit in the 
stack, not thread_stack objects. I suspect this is a typo.

2. Since "count" is a number of stack_head objects, pointer arithmetic 
involving it should be in terms of "stack_head *" pointers, not raw bytes 
("void *"). The stack high end is the last element of a "stack_head array" 
that forms the tack. (Note: And *not* "one past the end." You did not make 
this mistake, but I want to highlight it. The last stack_head element is 
just beyond the stack as seen by the thread, serving as a kind of metadata 
much like argc+argv+envp+aux on the main thread stack.)

Re: Stack head using the clone3 system call 24 days ago

From Christopher Wellons to ~skeeto/public-inbox

I'm glad to see that you've given this a shot! You're quite close, but 
there area a few subtle details to sort out.

First, unlike clone(), clone3() takes a pointer to the low end of the 
stack, which is mentioned on the man page. The original clone() does not 
know the stack size, so it takes a pointer to the high end, as otherwise 
the kernel could not find it. That simplified my demo, too. So you must 
modify newstack() to return both ends of the stack, the high end for you 
to populate and the low end to give to the kernel. I defined this to be 
returned from newthread():

typedef struct {
    void       *tail;
    stack_head *head;

Re: https://www.ele.uri.edu/faculty/vetter/Other-stuff/vi/009-index.html 24 days ago

From Christopher Wellons to ~skeeto/public-inbox

Thanks for the links, Ljubomir. I knew about Arabesque of course, but the 
Alan Zintz tutorial is new to me. When choosing a recommendation I look 
for a text that begins with the fundamentals followed by an opinionated 
(with good taste) guide of how to apply them. This old tutorial does not 
quite fit, but my usual recommendation, Practical Vim (Drew Neil), does 
exactly that. In another direction is the Vim manual, which is thorough 
and covers every last detail, but lacks an opinion on how to use all that 
power.

Re: An easy-to-implement, arena-friendly hash map a month ago

From Christopher Wellons to ~skeeto/public-inbox

> How did you figure this out?

(I just remembered I forgot to respond to this.) I wrote my first attempt 
in C++ using operator overloading in order to have nice ergonomics while I 
got the hang of relative pointers, and GCC's optimization punished me for 
misuse of char* pointer arithmetic:

https://lists.sr.ht/~skeeto/public-inbox/%3C20230902232504.hcihm5fdw6k7kejq%40gen2.localdomain%3E#%3C20230911223809.2rvs357olqfzt4d2@nullprogram.com%3E

While that was mostly a result of being cheeky in my test, using local 
variables rather than a proper arena, I expect it's necessary even with 
arenas when also using the "malloc" function attribute. It specifically 
declares that returned pointers are unrelated, and therefore cannot be 
compared using char*. So uintptr_t is really the only safe way to do it.

Re: An easy-to-implement, arena-friendly hash map a month ago

From Christopher Wellons to ~skeeto/public-inbox

Great question, Angel!

That part isn't so simple and pretty, which is why I had suggested an 
intrusive linked list. It turns it a hash trie into a "linked hash map" 
where the list order is independent of the hash trie, and iteration is as 
simple as traversing that list. It could track, say, insertion order, or 
you could sort it — mergesort goes well with linked lists — all without 
disrupting the underlying hash trie.

If you'd like to see hash trie iteration, I wrote an example iterator over 
an integer hash set (iter, newiter, iternext):

https://gist.github.com/skeeto/e416b3b07bf63792db91a5139526612e

Re: compile checking of NULL & macro magic a month ago

From Christopher Wellons to ~skeeto/public-inbox

Thanks for the video links, Flo. I hadn't yet come across the first, and 
there's a shocking amount of overlap with my own thoughts and writing the 
past year — even down to signed sizes. The slide "API Design: Modern C" at 
41:20 is a good summary, which emphasizes that "modern C" is more about 
style (esp. ZII, allocator aware) and attitude than new language features 
(emphasized _too_ much in the second video). That is, these techniques 
have all been possible for a quarter century. I'll reference this video 
myself in the future.

> What do you think of this addition and do you think it is useful?

At least for me, not useful, and the ugly syntax keeps me from using it 
even casually. With one exception, I just don't have trouble passing null 
pointers to functions that don't accept them. As Lucas Sas notes in the

Re: Two-in-one arenas a month ago

From Christopher Wellons to ~skeeto/public-inbox

> not entirely clear on the entire "alternating" business

Yeah, that's one of the major downsides. If, of all people, _you're_ not 
clear on it, then the typical person using it, or even just reading the 
code, will probably be unable to reason whether or not its use is correct. 
(Difficulty reasoning about code that should be straightforward and simple 
is one of my chief complaints about modern C++ after all.)

> The "fast realloc" trick Dennis shared on the list also wouldn't work 
> for the same reason.

Yup, definitely another cost.

Tangent: In preparation for a future article, I've revisited an old Go

Two-in-one arenas a month ago

From Christopher Wellons to ~skeeto/public-inbox

The other day I had a brief discussion with u/treefidgety, who did not 
like the idea of passing two arenas down the call stack any time there was 
a function that would need both. It got me thinking about how this might 
be avoided, and I came up with an idea of using one arena for both roles.

The core concept: An arena has two "ends" and we could use the other end 
for scratch allocations. Since callees/callers alternate arenas, which end 
is which depends on the position in the call stack. When passing an arena, 
the ends usually swap, and we can do this while keeping the two-pointer 
arena format. I call the ends "persist" and "scratch". Allocations that 
survive local scope are made from the former, and scoped allocations from 
the latter.

    typedef struct {

Re: A simple, arena-backed, generic dynamic array for C a month ago

From Christopher Wellons to ~skeeto/public-inbox

I don't know the history, but C and C++ generally regard null pointers as 
super special — very much not a valid pointer to a zero-sized object. I 
hadn't thought about it with qsort before, but other cases include fwrite 
and fread. C++ takes it even further: When the standard says a function 
accepts an "array" (e.g. std::ostream::write) you're not even allowed to 
use zero as a count/nmemb with a non-null pointer because arrays cannot be 
zero length. It's nuts, like they were trying to make gotchas.

Though whatever historical circumstance established this should not matter 
today, GCC uses it as an optimization hint. If a pointer is passed to one 
of these functions, then it's assumed not null, and that hint propagates 
forward potentially removing null pointer checks and such.

A half workaround is to write your own copy/move/set functions with your

Re: My personal C coding style as of late 2023 a month ago

From Christopher Wellons to ~skeeto/public-inbox

Thanks, Dmitrii! Yeah, I've used CSS Reset myself in the past. A cool, 
useful project.