Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

emutls_control

emutls.emutls_control
const emutls_control = extern struct

File

lib/compiler_rt/emutls.zig:204

Code

const emutls_control = extern struct {
    // A emutls_control value is a global value across all
    // threads. The threads shares the index of TLS variable. The data
    // array (containing address of allocated variables) is thread
    // specific and stored using pthread_setspecific().

    // size of the object in bytes
    size: gcc_word,

    // alignment of the object in bytes
    alignment: gcc_word,

    object: extern union {
        // data[index-1] is the object address / 0 = uninit
        index: usize,

        // object address, when in single thread env (not used)
        address: *anyopaque,
    },

    // null or non-zero initial value for the object
    default_value: ?*const anyopaque,

    // global Mutex used to serialize control.index initialization.
    var mutex: std.c.pthread_mutex_t = std.c.PTHREAD_MUTEX_INITIALIZER;

    // global counter for keeping track of requested indexes.
    // access should be done with mutex held.
    var next_index: usize = 1;

    /// Simple wrapper for global lock.
    fn lock() void {
        if (std.c.pthread_mutex_lock(&emutls_control.mutex) != .SUCCESS) {
            abort();
        }
    }

    /// Simple wrapper for global unlock.
    fn unlock() void {
        if (std.c.pthread_mutex_unlock(&emutls_control.mutex) != .SUCCESS) {
            abort();
        }
    }

    /// Helper to retrieve nad initialize global unique index per emutls variable.
    pub fn getIndex(self: *emutls_control) usize {
        // Two threads could race against the same emutls_control.

        // Use atomic for reading coherent value lockless.
        const index_lockless = @atomicLoad(usize, &self.object.index, .acquire);

        if (index_lockless != 0) {
            // index is already initialized, return it.
            return index_lockless;
        }

        // index is uninitialized: take global lock to avoid possible race.
        emutls_control.lock();
        defer emutls_control.unlock();

        const index_locked = self.object.index;
        if (index_locked != 0) {
            // we lost a race, but index is already initialized: nothing particular to do.
            return index_locked;
        }

        // Store a new index atomically (for having coherent index_lockless reading).
        @atomicStore(usize, &self.object.index, emutls_control.next_index, .release);

        // Increment the next available index
        emutls_control.next_index += 1;

        return self.object.index;
    }

    /// Simple helper for testing purpose.
    pub fn init(comptime T: type, default_value: ?*const T) emutls_control {
        return emutls_control{
            .size = @sizeOf(T),
            .alignment = @alignOf(T),
            .object = .{ .index = 0 },
            .default_value = @ptrCast(default_value),
        };
    }

    /// Get the pointer on allocated storage for emutls variable.
    pub fn getPointer(self: *emutls_control) *anyopaque {
        // ensure current_thread_storage initialization is done
        current_thread_storage.init();

        const index = self.getIndex();
        var array = current_thread_storage.getArray(index);

        return array.getPointer(index - 1, self);
    }

    /// Testing helper for retrieving typed pointer.
    pub fn get_typed_pointer(self: *emutls_control, comptime T: type) *T {
        assert(self.size == @sizeOf(T));
        assert(self.alignment == @alignOf(T));
        return @ptrCast(@alignCast(self.getPointer()));
    }
}