https://ziglang.org/documentation/master/std/#src/std/atomic.zig
I'd like to start working on http.zig thread-races and cant simply wrap worker._state in std.atomic.Value. Even tho the underlying @atomicLoad seems to support it. Should this be changed to regular struct, or am i missing something. It seems, that it was extern from the time it was added. https://github.com/ziglang/zig/commit/eb6975f0889db4c2c29d47bb959bc39bd0a9b167
#Is there any reason why atomic.Value is an extern struct?
1 messages · Page 1 of 1 (latest)
what is the type of your worker._state?
std.atomic.Value is only for basic types such as integers, bools, pointers, int backed enums, floats.
Not sure why exactly it's marked as extern, but probably to ensure that the atomic field indeed is C ABI compatible since atomic operations are resolved when generating machine code (self-hosted or LLVM backend). This is also why atomic operations rely on builtins instead of being part of zig std lib
its extern to have a defined layout when placed explictly in FFI stuff, or to guarantee data layout to avoid false sharing without overalignment
extern struct {
x: Atomic(u32),
_pad: [atomic.cache_line]u8,
data: T,
};
you can actually _: void align(atomic.cache_line) = {} to get the same thing
for http.zig, just make the state enum enum(u8)
this is unspecified tho as compiler is allowed to reorder fields for normal structs
perhaps
mb forgot to specify, enum {val1, val2, val3, val4} (i cant remember their names from the top of my head, but the default backing int was u2, if i explicitly specified u8 it worked, which was a quick and easy hack)
Oh i see, yeah for compatability better off turn it int u8. You could also intCast back to the original backing int so that rest of zig code can use u2
cant we promote zig structs to extern in this context?
i think this defeats the intended purpose, as i just wanted to quickly eliminate thread races in a purely zig code base, by wrapping all contended data
i mean intCast is fine if you control the API such that you can guarantee that only u2 range will be stored in the u8
That's the point of it - you assert that transition from u8 to u2 in this context is safe
But it is more of a hassle, so might as well just use u8 as backing int - it's the smallest unit of storage anyway on most systems
still seems kinda odd, to have to pay the penalty of ffi without relying on it :/
I mean it sort of is - CPU has specific constraints on values that can be operated on atomically, so it's modeled around them
why would passing an extern struct to anything C-land with an atomic even be desired? all the methodes become inaccesible and the type wont even show up in headers as anything special, right?
i mean you can pass a pointer (to an enum) dyrectly to @loadAtomic, so this is handeled at a later stage in the compiler
Does it work with u2 backed enum, have you tried? The type that is pointed to is still limited, though zig docs are bit vague on that:
T must be a pointer, a bool, a float, an integer or an enum.
as far as i've read into http.zig, at some points this enum (which had an implicit backing int) was accessed with atomic builtins
as far as im aware byte is the smallest addressable unit of memory (idk really at which point in the zig pipeline this is presumed), so addres of u2 will point to the whole byte and just act as another hint to llvm to dissregard all other illegal states
Wtf, here the state doesn't even have backing int. Which zig version is that?
compiles and works on 0.14
no, they mean different things. You can however store x: [@sizeOf(T)]u8 align(@alignOf(T)) in an extern struct then ptrCast it to *T
Then yeah, seems arbitrary to have extern on that atomic.Value struct, especially since it's single value. Perhaps designed to access foreign atomics directly
would you mind clarifying what extern means, in my mind it is a restriction on the layout and allowed types (to be somewhat C compatible), and thus if we think of it as a constraint, it can be passed down the hierarchy (this is a horribly bad idea, i figured out why now, you could have multiple variants of T or backward propagate extern constraint, creating problems in both cases);
but
const T = struct {...};
const TExportable = @extern(T);
const M = extern struct {...};
comptime {
assert(TExportable == M);
}
should be sane, where @extern(T:type) T would compile error on unrepresentable types and otherwise convert struct to extr struct?
fair point, id have to look into translate c artifacts and how that could be handeled in actual exports.
i mean this as it is wouldn't be exported in a way accessible from C. But one can use this as an interface to say atomic in a shared memory or to some struct returned from C
all fair points, i think that for now u8 as the backing int is the best solution and later on nag in the #bikeshed channel about creating 2 versions in stdlib or adding support for comptime struct to extern struct & packed struct translation
extern means "C layout"
normal structs means "compiler defined / undefined" layout. Some sort of extern-ify builtin wouldnt make sense since the externified struct still can have undefined memory layout, just a defined size and alignment.
i mean, id write my own externify function, but zig doesn't allow building arbitrary types in comptime, meaning, (not sure which one) decls or fields would be missing from an externified struct; thus the builtin proposal, to get around the restrictions, otherwise it would be just like defining a new struct with extern at the begining; maybe this is too rare of a usecace and crossing over into creating a bikeshed parkinglot
closing question for now, thank you all for great answers
uhh actually in this case i think comptime would allow generating extern struct. You can't generate functions, but you can construct types at comptime
create new struct type, mark it extern, copy the decls as is from original struct, have some methods that allow filling out that extern struct from existing regular struct. Of course would imply copying the values, since layouts wouldn't match for bitcast
theres the @Type builtin that lets you create structs. Defining it with extern at the start doesnt work if it contains things that also dont have a defined memory layout like other structs, slices, etc.
Don't have struct example, but here I generate enum at comptime, for example: https://smash.tase.lv/file?ci=tip&name=src/common/shmif.zig&ln=67-91
Right, child types would be biggest hassle at which point it would be simpler to design the extern version of such struct manually
i mean, if stdlib addopted this for some reason, recursive loop could be made to traverse to the bottom.
The basic demo works !!
:O