mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 09:22:22 -07:00
Merge branch 'odin-lang:master' into master
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
package hash
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
result = seed
|
||||
#no_bounds_check for b in data {
|
||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||
@@ -15,7 +15,7 @@ crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds
|
||||
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
data := data
|
||||
result := ~u64le(seed)
|
||||
|
||||
@@ -53,7 +53,7 @@ crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
|
||||
result := seed
|
||||
|
||||
@@ -70,7 +70,7 @@ crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
return result
|
||||
}
|
||||
|
||||
crc64_iso_3306_inverse :: proc(data: []byte, seed := u64(0)) -> u64 {
|
||||
crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 {
|
||||
result := #force_inline crc64_iso_3306(data, ~seed)
|
||||
return ~result
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package hash
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc := ~seed
|
||||
buffer := raw_data(data)
|
||||
length := len(data)
|
||||
@@ -323,7 +323,7 @@ crc32_table := [8][256]u32{
|
||||
|
||||
/*
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
result := ~u32(seed);
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
|
||||
+12
-12
@@ -4,7 +4,7 @@ import "core:mem"
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
|
||||
ADLER_CONST :: 65521
|
||||
|
||||
@@ -47,7 +47,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
|
||||
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
|
||||
@@ -55,7 +55,7 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
|
||||
return hash
|
||||
}
|
||||
|
||||
djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
|
||||
djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
|
||||
state := [4]u32{seed, seed, seed, seed}
|
||||
|
||||
s: u32 = 0
|
||||
@@ -74,7 +74,7 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -87,7 +87,7 @@ fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
@@ -95,7 +95,7 @@ fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
return h
|
||||
}
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193
|
||||
@@ -104,7 +104,7 @@ fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3
|
||||
@@ -113,7 +113,7 @@ fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash += u32(b)
|
||||
@@ -127,7 +127,7 @@ jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51
|
||||
c2_32: u32 : 0x1b873593
|
||||
|
||||
@@ -178,7 +178,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||
@(optimization_mode="speed")
|
||||
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
|
||||
@@ -219,7 +219,7 @@ murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||
@(optimization_mode="speed")
|
||||
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
@@ -287,7 +287,7 @@ murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = u32(b) + (hash<<6) + (hash<<16) - hash
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
package hash
|
||||
|
||||
ginger_hash8 :: proc(x: u8) -> u8 {
|
||||
ginger_hash8 :: proc "contextless" (x: u8) -> u8 {
|
||||
h := x * 251
|
||||
h += ~(x << 3)
|
||||
h ~= (x >> 1)
|
||||
@@ -11,7 +11,7 @@ ginger_hash8 :: proc(x: u8) -> u8 {
|
||||
}
|
||||
|
||||
|
||||
ginger_hash16 :: proc(x: u16) -> u16 {
|
||||
ginger_hash16 :: proc "contextless" (x: u16) -> u16 {
|
||||
z := (x << 8) | (x >> 8)
|
||||
h := z
|
||||
h += ~(z << 5)
|
||||
@@ -24,14 +24,14 @@ ginger_hash16 :: proc(x: u16) -> u16 {
|
||||
}
|
||||
|
||||
|
||||
ginger8 :: proc(data: []byte) -> u8 {
|
||||
ginger8 :: proc "contextless" (data: []byte) -> u8 {
|
||||
h := ginger_hash8(0)
|
||||
for b in data {
|
||||
h ~= ginger_hash8(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
ginger16 :: proc(data: []byte) -> u16 {
|
||||
ginger16 :: proc "contextless" (data: []byte) -> u16 {
|
||||
h := ginger_hash16(0)
|
||||
for b in data {
|
||||
h ~= ginger_hash16(u16(b))
|
||||
|
||||
@@ -129,7 +129,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
|
||||
}
|
||||
|
||||
XXH3_destroy_state :: proc(state: ^XXH3_state, allocator := context.allocator) -> (err: Error) {
|
||||
free(state)
|
||||
free(state, allocator)
|
||||
return .None
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,15 @@ xxh_u32 :: u32
|
||||
XXH32_DEFAULT_SEED :: XXH32_hash(0)
|
||||
|
||||
XXH32_state :: struct {
|
||||
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
|
||||
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
|
||||
v1: XXH32_hash, /*!< First accumulator lane */
|
||||
v2: XXH32_hash, /*!< Second accumulator lane */
|
||||
v3: XXH32_hash, /*!< Third accumulator lane */
|
||||
v4: XXH32_hash, /*!< Fourth accumulator lane */
|
||||
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
|
||||
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
|
||||
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
|
||||
v1: XXH32_hash, /*!< First accumulator lane */
|
||||
v2: XXH32_hash, /*!< Second accumulator lane */
|
||||
v3: XXH32_hash, /*!< Third accumulator lane */
|
||||
v4: XXH32_hash, /*!< Fourth accumulator lane */
|
||||
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
|
||||
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
}
|
||||
|
||||
XXH32_canonical :: struct {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/darwin"
|
||||
import "core:time"
|
||||
|
||||
foreign import System "system:System.framework"
|
||||
@@ -29,8 +30,29 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
|
||||
s: i32
|
||||
if duration > 0 {
|
||||
s = darwin.os_sync_wait_on_address_with_timeout(f, u64(expected), size_of(Futex), {}, .MACH_ABSOLUTE_TIME, u64(duration))
|
||||
} else {
|
||||
s = darwin.os_sync_wait_on_address(f, u64(expected), size_of(Futex), {})
|
||||
}
|
||||
|
||||
if s >= 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
switch darwin.errno() {
|
||||
case -EINTR, -EFAULT:
|
||||
return true
|
||||
case -ETIMEDOUT:
|
||||
return false
|
||||
case:
|
||||
_panic("darwin.os_sync_wait_on_address_with_timeout failure")
|
||||
}
|
||||
} else {
|
||||
|
||||
timeout_ns := u32(duration) * 1000
|
||||
|
||||
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
|
||||
if s >= 0 {
|
||||
return true
|
||||
@@ -45,9 +67,27 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
|
||||
loop: for {
|
||||
s := darwin.os_sync_wake_by_address_any(f, size_of(Futex), {})
|
||||
if s >= 0 {
|
||||
return
|
||||
}
|
||||
switch darwin.errno() {
|
||||
case -EINTR, -EFAULT:
|
||||
continue loop
|
||||
case -ENOENT:
|
||||
return
|
||||
case:
|
||||
_panic("darwin.os_sync_wake_by_address_any failure")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
|
||||
if s >= 0 {
|
||||
@@ -62,9 +102,28 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
_panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
|
||||
loop: for {
|
||||
s := darwin.os_sync_wake_by_address_all(f, size_of(Futex), {})
|
||||
if s >= 0 {
|
||||
return
|
||||
}
|
||||
switch darwin.errno() {
|
||||
case -EINTR, -EFAULT:
|
||||
continue loop
|
||||
case -ENOENT:
|
||||
return
|
||||
case:
|
||||
_panic("darwin.os_sync_wake_by_address_all failure")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
|
||||
if s >= 0 {
|
||||
@@ -79,5 +138,6 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
_panic("futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package darwin
|
||||
|
||||
import "core:c"
|
||||
|
||||
foreign import system "system:System.framework"
|
||||
|
||||
Bool :: b8
|
||||
|
||||
RUsage :: struct {
|
||||
@@ -24,3 +26,10 @@ RUsage :: struct {
|
||||
ru_nivcsw: c.long,
|
||||
}
|
||||
|
||||
foreign system {
|
||||
__error :: proc() -> ^i32 ---
|
||||
}
|
||||
|
||||
errno :: #force_inline proc "contextless" () -> i32 {
|
||||
return __error()^
|
||||
}
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
package darwin
|
||||
|
||||
foreign import system "system:System.framework"
|
||||
|
||||
// #define OS_WAIT_ON_ADDR_AVAILABILITY \
|
||||
// __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
|
||||
when ODIN_OS == .Darwin {
|
||||
when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION > 17_04_00 {
|
||||
WAIT_ON_ADDRESS_AVAILABLE :: true
|
||||
} else when ODIN_MINIMUM_OS_VERSION > 14_04_00 {
|
||||
WAIT_ON_ADDRESS_AVAILABLE :: true
|
||||
} else {
|
||||
WAIT_ON_ADDRESS_AVAILABLE :: false
|
||||
}
|
||||
} else {
|
||||
WAIT_ON_ADDRESS_AVAILABLE :: false
|
||||
}
|
||||
|
||||
os_sync_wait_on_address_flag :: enum u32 {
|
||||
// This flag should be used as a default flag when no other flags listed below are required.
|
||||
NONE,
|
||||
|
||||
// This flag should be used when synchronizing among multiple processes by
|
||||
// placing the @addr passed to os_sync_wait_on_address and its variants
|
||||
// in a shared memory region.
|
||||
//
|
||||
// When using this flag, it is important to pass OS_SYNC_WAKE_BY_ADDRESS_SHARED
|
||||
// flag along with the exact same @addr to os_sync_wake_by_address_any and
|
||||
// its variants to correctly find and wake up blocked waiters on the @addr.
|
||||
//
|
||||
// This flag should not be used when synchronizing among multiple threads of
|
||||
// a single process. It allows the kernel to perform performance optimizations
|
||||
// as the @addr is local to the calling process.
|
||||
SHARED,
|
||||
}
|
||||
|
||||
os_sync_wait_on_address_flags :: bit_set[os_sync_wait_on_address_flag; u32]
|
||||
|
||||
os_sync_wake_by_address_flag :: enum u32 {
|
||||
// This flag should be used as a default flag when no other flags listed below are required.
|
||||
NONE,
|
||||
|
||||
// This flag should be used when synchronizing among multiple processes by
|
||||
// placing the @addr passed to os_sync_wake_by_address_any and its variants
|
||||
// in a shared memory region.
|
||||
//
|
||||
// When using this flag, it is important to pass OS_SYNC_WAIT_ON_ADDRESS_SHARED
|
||||
// flag along with the exact same @addr to os_sync_wait_on_address and
|
||||
// its variants to correctly find and wake up blocked waiters on the @addr.
|
||||
//
|
||||
// This flag should not be used when synchronizing among multiple threads of
|
||||
// a single process. It allows the kernel to perform performance optimizations
|
||||
// as the @addr is local the calling process.
|
||||
SHARED,
|
||||
}
|
||||
|
||||
os_sync_wake_by_address_flags :: bit_set[os_sync_wake_by_address_flag; u32]
|
||||
|
||||
os_clockid :: enum u32 {
|
||||
MACH_ABSOLUTE_TIME = 32,
|
||||
}
|
||||
|
||||
foreign system {
|
||||
// This function provides an atomic compare-and-wait functionality that
|
||||
// can be used to implement other higher level synchronization primitives.
|
||||
//
|
||||
// It reads a value from @addr, compares it to expected @value and blocks
|
||||
// the calling thread if they are equal. This sequence of operations is
|
||||
// done atomically with respect to other concurrent operations that can
|
||||
// be performed on this @addr by other threads using this same function
|
||||
// or os_sync_wake_by_addr variants. At this point, the blocked calling
|
||||
// thread is considered to be a waiter on this @addr, waiting to be woken
|
||||
// up by a call to os_sync_wake_by_addr variants. If the value at @addr
|
||||
// turns out to be different than expected, the calling thread returns
|
||||
// immediately without blocking.
|
||||
//
|
||||
// This function is expected to be used for implementing synchronization
|
||||
// primitives that do not have a sense of ownership (e.g. condition
|
||||
// variables, semaphores) as it does not provide priority inversion avoidance.
|
||||
// For locking primitives, it is recommended that you use existing OS
|
||||
// primitives such as os_unfair_lock API family / pthread mutex or
|
||||
// std::mutex.
|
||||
//
|
||||
// @param addr
|
||||
// The userspace address to be used for atomic compare-and-wait.
|
||||
// This address must be aligned to @size.
|
||||
//
|
||||
// @param value
|
||||
// The value expected at @addr.
|
||||
//
|
||||
// @param size
|
||||
// The size of @value, in bytes. This can be either 4 or 8 today.
|
||||
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
|
||||
//
|
||||
// @param flags
|
||||
// Flags to alter behavior of os_sync_wait_on_address.
|
||||
// See os_sync_wait_on_address_flags_t.
|
||||
//
|
||||
// @return
|
||||
// If the calling thread is woken up by a call to os_sync_wake_by_addr
|
||||
// variants or the value at @addr is different than expected, this function
|
||||
// returns successfully and the return value indicates the number
|
||||
// of outstanding waiters blocked on this address.
|
||||
// In the event of an error, returns -1 with errno set to indicate the error.
|
||||
//
|
||||
// EINVAL : Invalid flags or size.
|
||||
// EINVAL : The @addr passed is NULL or misaligned.
|
||||
// EINVAL : The operation associated with existing kernel state
|
||||
// at this @addr is inconsistent with what the caller
|
||||
// has requested.
|
||||
// It is important to make sure consistent values are
|
||||
// passed across wait and wake APIs for @addr, @size
|
||||
// and the shared memory specification
|
||||
// (See os_sync_wait_on_address_flags_t).
|
||||
//
|
||||
// It is possible for the os_sync_wait_on_address and its variants to perform
|
||||
// an early return in the event of following errors where user may want to
|
||||
// re-try the wait operation. E.g. low memory conditions could cause such early
|
||||
// return.
|
||||
// It is important to read the current value at the @addr before re-trying
|
||||
// to ensure that the new value still requires waiting on @addr.
|
||||
//
|
||||
// ENOMEM : Unable to allocate memory for kernel internal data
|
||||
// structures.
|
||||
// EINTR : The syscall was interrupted / spurious wake up.
|
||||
// EFAULT : Unable to read value from the @addr. Kernel copyin failed.
|
||||
// It is possible to receive EFAULT error in following cases:
|
||||
// 1. The @addr is an invalid address. This is a programmer error.
|
||||
// 2. The @addr is valid; but, this is a transient error such as
|
||||
// due to low memory conditions. User may want to re-try the wait
|
||||
// operation.
|
||||
// Following code snippet illustrates a possible re-try loop.
|
||||
// <code>
|
||||
// retry:
|
||||
// current = atomic_load_explicit(addr, memory_order_relaxed);
|
||||
// if (current != expected) {
|
||||
// int ret = os_sync_wait_on_address(addr, current, size, flags);
|
||||
// if ((ret < 0) && ((errno == EINTR) || (errno == EFAULT))) {
|
||||
// goto retry;
|
||||
// }
|
||||
// }
|
||||
// </code>
|
||||
os_sync_wait_on_address :: proc(
|
||||
addr: rawptr,
|
||||
value: u64,
|
||||
size: uint,
|
||||
flags: os_sync_wait_on_address_flags,
|
||||
) -> i32 ---
|
||||
|
||||
// This function is a variant of os_sync_wait_on_address that
|
||||
// allows the calling thread to specify a deadline
|
||||
// until which it is willing to block.
|
||||
//
|
||||
// @param addr
|
||||
// The userspace address to be used for atomic compare-and-wait.
|
||||
// This address must be aligned to @size.
|
||||
//
|
||||
// @param value
|
||||
// The value expected at @addr.
|
||||
//
|
||||
// @param size
|
||||
// The size of @value, in bytes. This can be either 4 or 8 today.
|
||||
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
|
||||
//
|
||||
// @param flags
|
||||
// Flags to alter behavior of os_sync_wait_on_address_with_deadline.
|
||||
// See os_sync_wait_on_address_flags_t.
|
||||
//
|
||||
// @param clockid
|
||||
// This value anchors @deadline argument to a specific clock id.
|
||||
// See os_clockid_t.
|
||||
//
|
||||
// @param deadline
|
||||
// This value is used to specify a deadline until which the calling
|
||||
// thread is willing to block.
|
||||
// Passing zero for the @deadline results in an error being returned.
|
||||
// It is recommended to use os_sync_wait_on_address API to block
|
||||
// indefinitely until woken up by a call to os_sync_wake_by_address_any
|
||||
// or os_sync_wake_by_address_all APIs.
|
||||
//
|
||||
// @return
|
||||
// If the calling thread is woken up by a call to os_sync_wake_by_addr
|
||||
// variants or the value at @addr is different than expected, this function
|
||||
// returns successfully and the return value indicates the number
|
||||
// of outstanding waiters blocked on this address.
|
||||
// In the event of an error, returns -1 with errno set to indicate the error.
|
||||
//
|
||||
// In addition to errors returned by os_sync_wait_on_address, this function
|
||||
// can return the following additional error codes.
|
||||
//
|
||||
// EINVAL : Invalid clock id.
|
||||
// EINVAL : The @deadline passed is 0.
|
||||
// ETIMEDOUT : Deadline expired.
|
||||
os_sync_wait_on_address_with_deadline :: proc(
|
||||
addr: rawptr,
|
||||
value: u64,
|
||||
size: uint,
|
||||
flags: os_sync_wait_on_address_flags,
|
||||
clockid: os_clockid,
|
||||
deadline: u64,
|
||||
) -> i32 ---
|
||||
|
||||
// This function is a variant of os_sync_wait_on_address that
|
||||
// allows the calling thread to specify a timeout
|
||||
// until which it is willing to block.
|
||||
//
|
||||
// @param addr
|
||||
// The userspace address to be used for atomic compare-and-wait.
|
||||
// This address must be aligned to @size.
|
||||
//
|
||||
// @param value
|
||||
// The value expected at @addr.
|
||||
//
|
||||
// @param size
|
||||
// The size of @value, in bytes. This can be either 4 or 8 today.
|
||||
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
|
||||
//
|
||||
// @param flags
|
||||
// Flags to alter behavior of os_sync_wait_on_address_with_timeout.
|
||||
// See os_sync_wait_on_address_flags_t.
|
||||
//
|
||||
// @param clockid
|
||||
// This value anchors @timeout_ns argument to a specific clock id.
|
||||
// See os_clockid_t.
|
||||
//
|
||||
// @param timeout_ns
|
||||
// This value is used to specify a timeout in nanoseconds until which
|
||||
// the calling thread is willing to block.
|
||||
// Passing zero for the @timeout_ns results in an error being returned.
|
||||
// It is recommended to use os_sync_wait_on_address API to block
|
||||
// indefinitely until woken up by a call to os_sync_wake_by_address_any
|
||||
// or os_sync_wake_by_address_all APIs.
|
||||
//
|
||||
// @return
|
||||
// If the calling thread is woken up by a call to os_sync_wake_by_address
|
||||
// variants or the value at @addr is different than expected, this function
|
||||
// returns successfully and the return value indicates the number
|
||||
// of outstanding waiters blocked on this address.
|
||||
// In the event of an error, returns -1 with errno set to indicate the error.
|
||||
//
|
||||
// In addition to errors returned by os_sync_wait_on_address, this function
|
||||
// can return the following additional error codes.
|
||||
//
|
||||
// EINVAL : Invalid clock id.
|
||||
// EINVAL : The @timeout_ns passed is 0.
|
||||
// ETIMEDOUT : Timeout expired.
|
||||
os_sync_wait_on_address_with_timeout :: proc(
|
||||
addr: rawptr,
|
||||
value: u64,
|
||||
size: uint,
|
||||
flags: os_sync_wait_on_address_flags,
|
||||
clockid: os_clockid,
|
||||
timeout_ns: u64,
|
||||
) -> i32 ---
|
||||
|
||||
// This function wakes up one waiter out of all those blocked in os_sync_wait_on_address
|
||||
// or its variants on the @addr. No guarantee is provided about which
|
||||
// specific waiter is woken up.
|
||||
//
|
||||
// @param addr
|
||||
// The userspace address to be used for waking up the blocked waiter.
|
||||
// It should be same as what is passed to os_sync_wait_on_address or its variants.
|
||||
//
|
||||
// @param size
|
||||
// The size of lock value, in bytes. This can be either 4 or 8 today.
|
||||
// It should be same as what is passed to os_sync_wait_on_address or its variants.
|
||||
//
|
||||
// @param flags
|
||||
// Flags to alter behavior of os_sync_wake_by_address_any.
|
||||
// See os_sync_wake_by_address_flags_t.
|
||||
//
|
||||
// @return
|
||||
// Returns 0 on success.
|
||||
// In the event of an error, returns -1 with errno set to indicate the error.
|
||||
//
|
||||
// EINVAL : Invalid flags or size.
|
||||
// EINVAL : The @addr passed is NULL.
|
||||
// EINVAL : The operation associated with existing kernel state
|
||||
// at this @addr is inconsistent with what caller
|
||||
// has requested.
|
||||
// It is important to make sure consistent values are
|
||||
// passed across wait and wake APIs for @addr, @size
|
||||
// and the shared memory specification
|
||||
// (See os_sync_wake_by_address_flags_t).
|
||||
// ENOENT : No waiter(s) found waiting on the @addr.
|
||||
os_sync_wake_by_address_any :: proc(addr: rawptr, size: uint, flags: os_sync_wait_on_address_flags) -> i32 ---
|
||||
|
||||
// This function is a variant of os_sync_wake_by_address_any that wakes up all waiters
|
||||
// blocked in os_sync_wait_on_address or its variants.
|
||||
//
|
||||
// @param addr
|
||||
// The userspace address to be used for waking up the blocked waiters.
|
||||
// It should be same as what is passed to os_sync_wait_on_address or its variants.
|
||||
//
|
||||
// @param size
|
||||
// The size of lock value, in bytes. This can be either 4 or 8 today.
|
||||
// It should be same as what is passed to os_sync_wait_on_address or its variants.
|
||||
//
|
||||
// @param flags
|
||||
// Flags to alter behavior of os_sync_wake_by_address_all.
|
||||
// See os_sync_wake_by_address_flags_t.
|
||||
//
|
||||
// @return
|
||||
// Returns 0 on success.
|
||||
// In the event of an error, returns -1 with errno set to indicate the error.
|
||||
//
|
||||
// This function returns same error codes as returned by os_sync_wait_on_address.
|
||||
os_sync_wake_by_address_all :: proc(addr: rawptr, size: uint, flags: os_sync_wait_on_address_flags) -> i32 ---
|
||||
}
|
||||
@@ -1097,6 +1097,15 @@ gb_internal void init_universal(void) {
|
||||
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
|
||||
}
|
||||
|
||||
{
|
||||
int minimum_os_version = 0;
|
||||
if (build_context.minimum_os_version_string != "") {
|
||||
int major, minor, revision = 0;
|
||||
sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
|
||||
minimum_os_version = (major*10000)+(minor*100)+revision;
|
||||
}
|
||||
add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
|
||||
}
|
||||
|
||||
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
|
||||
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
|
||||
|
||||
+4
-2
@@ -508,8 +508,10 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
|
||||
}
|
||||
|
||||
// This points the linker to where the entry point is
|
||||
link_settings = gb_string_appendc(link_settings, "-e _main ");
|
||||
if (build_context.build_mode != BuildMode_DynamicLibrary) {
|
||||
// This points the linker to where the entry point is
|
||||
link_settings = gb_string_appendc(link_settings, "-e _main ");
|
||||
}
|
||||
}
|
||||
|
||||
if (!build_context.no_crt) {
|
||||
|
||||
@@ -2885,6 +2885,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
elem = parse_expr(f, true);
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
if (elem == nullptr) {
|
||||
syntax_error(token, "Expected a type or range, got nothing");
|
||||
}
|
||||
|
||||
if (allow_token(f, Token_Semicolon)) {
|
||||
underlying = parse_type(f);
|
||||
} else if (allow_token(f, Token_Comma)) {
|
||||
@@ -2894,6 +2898,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
underlying = parse_type(f);
|
||||
}
|
||||
|
||||
|
||||
expect_token(f, Token_CloseBracket);
|
||||
return ast_bit_set_type(f, token, elem, underlying);
|
||||
}
|
||||
@@ -5009,6 +5014,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
|
||||
case Token_Xor:
|
||||
case Token_Not:
|
||||
case Token_And:
|
||||
case Token_Mul: // Used for error handling when people do C-like things
|
||||
s = parse_simple_stmt(f, StmtAllowFlag_Label);
|
||||
expect_semicolon(f);
|
||||
return s;
|
||||
|
||||
@@ -758,6 +758,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#if __has_include(<os/os_sync_wait_on_address.h>)
|
||||
#define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
#include <os/os_sync_wait_on_address.h>
|
||||
#endif
|
||||
|
||||
#define UL_COMPARE_AND_WAIT 0x00000001
|
||||
#define ULF_NO_ERRNO 0x01000000
|
||||
|
||||
@@ -765,6 +770,23 @@ extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint
|
||||
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
|
||||
|
||||
gb_internal void futex_signal(Futex *f) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
if (errno == EINTR || errno == EFAULT) {
|
||||
continue;
|
||||
}
|
||||
if (errno == ENOENT) {
|
||||
return;
|
||||
}
|
||||
GB_PANIC("Failed in futex wake %d %d!\n", ret, errno);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
for (;;) {
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
|
||||
if (ret >= 0) {
|
||||
@@ -778,9 +800,29 @@ gb_internal void futex_signal(Futex *f) {
|
||||
}
|
||||
GB_PANIC("Failed in futex wake!\n");
|
||||
}
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal void futex_broadcast(Futex *f) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
if (errno == EINTR || errno == EFAULT) {
|
||||
continue;
|
||||
}
|
||||
if (errno == ENOENT) {
|
||||
return;
|
||||
}
|
||||
GB_PANIC("Failed in futext wake %d %d!\n", ret, errno);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
for (;;) {
|
||||
enum { ULF_WAKE_ALL = 0x00000100 };
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
|
||||
@@ -795,9 +837,32 @@ gb_internal void futex_broadcast(Futex *f) {
|
||||
}
|
||||
GB_PANIC("Failed in futex wake!\n");
|
||||
}
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_NONE);
|
||||
if (ret >= 0) {
|
||||
if (*f != val) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (errno == EINTR || errno == EFAULT) {
|
||||
continue;
|
||||
}
|
||||
if (errno == ENOENT) {
|
||||
return;
|
||||
}
|
||||
GB_PANIC("Failed in futex wait %d %d!\n", ret, errno);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
for (;;) {
|
||||
int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
|
||||
if (ret >= 0) {
|
||||
@@ -815,7 +880,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
|
||||
GB_PANIC("Failed in futex wait!\n");
|
||||
}
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
gb_internal void futex_signal(Futex *f) {
|
||||
|
||||
Reference in New Issue
Block a user