Problem with sigprocmask() when linking libc

Hi, I run into somewhat strange problem with sigprocmask().
Here is source code:

// spm.zig
const std = @import("std");
const os = std.os;
const sigProcMask = os.sigprocmask;
const SigSet = os.sigset_t;
const SIG = os.SIG;

pub fn main() void {
    var sset: SigSet = os.empty_sigset;
    os.linux.sigaddset(&sset, SIG.TERM);
    sigProcMask(SIG.BLOCK, &sset, null);
}

It compiles successfully with zig build-exe spm.zig.
But when linking with libc, I get this error:

$ /opt/zig/zig build-exe spm.zig -lc
/opt/zig/lib/std/os.zig:5503:38: error: expected type 'c_int', found 'u32'
    switch (errno(system.sigprocmask(flags, set, oldset))) {
                                     ^~~~~
/opt/zig/lib/std/os.zig:5503:38: note: signed 32-bit int cannot represent all possible unsigned 32-bit values
referenced by:
    sigProcMask: spm.zig:4:23
    main: spm.zig:11:5

How can I fix this?
Casting SIG.BLOCK to c_int does not help.

Your example did not compile for other reasons here, so I simplified it as follows:

const std = @import("std");

pub fn main() void {
    var sset: std.os.sigset_t = std.os.empty_sigset;
    std.os.linux.sigaddset(&sset, std.os.SIG.TERM);
    _ = std.os.linux.sigprocmask(std.os.SIG.BLOCK, &sset, null);
}

and this seems to compile and run just fine. :person_shrugging:

1 Like
const std = @import("std");
const os = std.os;
const SigSet = os.sigset_t;
const SIG = os.SIG;

pub fn main() void {
    var sset: SigSet = os.empty_sigset;
    os.linux.sigaddset(&sset, SIG.TERM);
    _ = std.c.sigprocmask(SIG.BLOCK, &sset, null);
}

is also compiles ok with -lc

Without -lc everything is ok with the example.

The example doesn’t work for me with or without -lc (I’m on Zig 0.9.1, should that be relevant), but I do admit I missed the -lc part of the issue on first reading :innocent:

The issue I get with your first code snippet is that std.os.sigprocmask() doesn’t exist – you have to get it from the system-specific implementation, if any, so either directly std.os.linux.sigprocmask() or std.c.sigprocmask(), or implicitly via std.os.system.sigprocmask(), as std.os.system will just alias the system-specific struct that contains the underlying syscalls.

Fixing that, and the discarded value from the sigProcMask() call, it compiles fine, and I don’t get any errors from c_int conversions. Can’t reproduce :slight_smile:

Yes, this version uses stage1 compiler by default (it may behave differently sometimes).
The version I am using is 0.10.0-dev.4212+34835bbbc

Right, I can reproduce on the latest master build. The issue is that Zig seems to have grown a std.os.sigprocmask() convenience wrapper which delegates to the underlying system-specific sigprocmask() and translates errno into an actual Zig error…

…but, the underlying std.c.sigprocmask() expects a c_int as its first parameter, while the convenience wrapper takes that value as a u32, thus the type clash.

This sounds like a bug in the convenience wrappers: the reason an @as(c_int, std.os.SIG.BLOCK) type coercion won’t help is that the method signature takes a u32 for that parameter, so it’ll be immediately type coerced back from c_int to u32

A cheap workaround is to copy the stdlib’s implementation in your own code and change the parameter type, but this ought to be fixed in the stdlib instead. Maybe you can open an issue on GitHub detailing the problem :innocent:

1 Like

Of course, I just waited for some reply similar to yours to confirm my suspicion that it might be a snag in stdlib.

bug is confirmed