Merge branch 'master' into windows-llvm-13.0.0

This commit is contained in:
gingerBill
2022-03-31 16:26:27 +01:00
120 changed files with 6388 additions and 3538 deletions

View File

@@ -38,6 +38,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin issues tests
run: tests/issues/run.sh
timeout-minutes: 10
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
@@ -87,6 +90,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin issues tests
run: tests/issues/run.sh
timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
timeout-minutes: 10
@@ -153,6 +159,12 @@ jobs:
cd tests\core\math\big
call build.bat
timeout-minutes: 10
- name: Odin issues tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
call tests\issues\run.bat
timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |

View File

@@ -1,74 +1,3 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
LDFLAGS=-pthread -lm -lstdc++
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
ARCH=$(shell uname -m)
LLVM_CONFIG=llvm-config
# allow for arm only llvm's with version 13
ifeq ($(ARCH), arm64)
LLVM_VERSIONS = "13.%.%"
else
# allow for x86 / amd64 all llvm versions begining from 11
LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
endif
LLVM_VERSION_PATTERN_SEPERATOR = )|(
LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
ifeq ($(ARCH), arm64)
$(error "Requirement: llvm-config must be base version 13 for arm64")
else
$(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
endif
endif
LDFLAGS:=$(LDFLAGS) -liconv -ldl
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
LLVM_CONFIG=llvm-config-11-64
else
ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
$(error "Requirement: llvm-config must be version 11")
endif
endif
LDFLAGS:=$(LDFLAGS) -ldl
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
ifeq ($(OS), OpenBSD)
LLVM_CONFIG=/usr/local/bin/llvm-config
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
ifeq ($(OS), FreeBSD)
LLVM_CONFIG=/usr/local/bin/llvm-config11
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
demo:
@@ -78,13 +7,13 @@ report:
./odin report
debug:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
./build_odin.sh debug
release:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
./build_odin.sh release
release_native:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
./build_odin.sh release-native
nightly:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
./build_odin.sh nightly

View File

@@ -41,6 +41,13 @@ config_darwin() {
LDFLAGS="$LDFLAGS -lLLVM-C"
}
config_freebsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config11
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
}
config_openbsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config
@@ -108,6 +115,9 @@ Darwin)
OpenBSD)
config_openbsd
;;
FreeBSD)
config_freebsd
;;
*)
panic "Platform unsupported!"
esac

View File

@@ -8,6 +8,7 @@ import "core:intrinsics"
// Extra errors returns by scanning procedures
Scanner_Extra_Error :: enum i32 {
None,
Negative_Advance,
Advanced_Too_Far,
Bad_Read_Count,
@@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 {
Too_Short,
}
Scanner_Error :: union {
Scanner_Error :: union #shared_nil {
io.Error,
Scanner_Extra_Error,
}
@@ -68,7 +69,7 @@ scanner_destroy :: proc(s: ^Scanner) {
// Returns the first non-EOF error that was encounted by the scanner
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err {
case .EOF, .None:
case .EOF, nil:
return nil
}
return s._err
@@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string {
// scanner_scan advances the scanner
scanner_scan :: proc(s: ^Scanner) -> bool {
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
err := err
if err == .None {
err = nil
}
switch s._err {
case nil, .EOF:
s._err = err

View File

@@ -47,24 +47,25 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
// 7.17.4 Fences
atomic_thread_fence :: #force_inline proc(order: memory_order) {
switch (order) {
case .relaxed:
return
case .consume:
intrinsics.atomic_fence_acq()
case .acquire:
intrinsics.atomic_fence_acq()
case .release:
intrinsics.atomic_fence_rel()
case .acq_rel:
intrinsics.atomic_fence_acqrel()
case .seq_cst:
intrinsics.atomic_fence_acqrel()
assert(order != .relaxed)
assert(order != .consume)
#partial switch order {
case .acquire: intrinsics.atomic_thread_fence(.Acquire)
case .release: intrinsics.atomic_thread_fence(.Release)
case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
}
}
atomic_signal_fence :: #force_inline proc(order: memory_order) {
atomic_thread_fence(order)
assert(order != .relaxed)
assert(order != .consume)
#partial switch order {
case .acquire: intrinsics.atomic_signal_fence(.Acquire)
case .release: intrinsics.atomic_signal_fence(.Release)
case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
}
}
// 7.17.5 Lock-free property
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
assert(order != .acquire)
assert(order != .acq_rel)
#partial switch (order) {
case .relaxed:
intrinsics.atomic_store_relaxed(object, desired)
case .release:
intrinsics.atomic_store_rel(object, desired)
case .seq_cst:
intrinsics.atomic_store(object, desired)
#partial switch order {
case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
}
}
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
assert(order != .release)
assert(order != .acq_rel)
#partial switch (order) {
case .relaxed:
return intrinsics.atomic_load_relaxed(object)
case .consume:
return intrinsics.atomic_load_acq(object)
case .acquire:
return intrinsics.atomic_load_acq(object)
case .seq_cst:
return intrinsics.atomic_load(object)
#partial switch order {
case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
}
}
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
return intrinsics.atomic_xchg(object, desired)
return intrinsics.atomic_exchange(object, desired)
}
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xchg_relaxed(object, desired)
case .consume:
return intrinsics.atomic_xchg_acq(object, desired)
case .acquire:
return intrinsics.atomic_xchg_acq(object, desired)
case .release:
return intrinsics.atomic_xchg_rel(object, desired)
case .acq_rel:
return intrinsics.atomic_xchg_acqrel(object, desired)
case .seq_cst:
return intrinsics.atomic_xchg(object, desired)
switch order {
case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
}
return false
}
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
// [success = seq_cst, failure = acquire] => failacq
// [success = acquire, failure = relaxed] => acq_failrelaxed
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
#partial switch (failure) {
#partial switch failure {
case .seq_cst:
assert(success != .relaxed)
#partial switch (success) {
#partial switch success {
case .seq_cst:
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
#partial switch (success) {
#partial switch success {
case .relaxed:
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
fallthrough
assert(success == .seq_cst)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
#partial switch (failure) {
#partial switch failure {
case .seq_cst:
assert(success != .relaxed)
#partial switch (success) {
#partial switch success {
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
#partial switch (success) {
#partial switch success {
case .relaxed:
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
fallthrough
assert(success == .seq_cst)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_add_relaxed(object, operand)
case .consume:
return intrinsics.atomic_add_acq(object, operand)
case .acquire:
return intrinsics.atomic_add_acq(object, operand)
case .release:
return intrinsics.atomic_add_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_add_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_add(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
}
}
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_sub_relaxed(object, operand)
case .consume:
return intrinsics.atomic_sub_acq(object, operand)
case .acquire:
return intrinsics.atomic_sub_acq(object, operand)
case .release:
return intrinsics.atomic_sub_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_sub_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_sub(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
}
}
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_or_relaxed(object, operand)
case .consume:
return intrinsics.atomic_or_acq(object, operand)
case .acquire:
return intrinsics.atomic_or_acq(object, operand)
case .release:
return intrinsics.atomic_or_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_or_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_or(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
}
}
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xor_relaxed(object, operand)
case .consume:
return intrinsics.atomic_xor_acq(object, operand)
case .acquire:
return intrinsics.atomic_xor_acq(object, operand)
case .release:
return intrinsics.atomic_xor_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_xor_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_xor(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
}
}
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_and(object, operand)
}
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_and_relaxed(object, operand)
case .consume:
return intrinsics.atomic_and_acq(object, operand)
case .acquire:
return intrinsics.atomic_and_acq(object, operand)
case .release:
return intrinsics.atomic_and_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_and_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_and(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
}
}

View File

@@ -47,7 +47,7 @@ when size_of(uintptr) == 8 {
}
Error :: union {
Error :: union #shared_nil {
General_Error,
Deflate_Error,
ZLIB_Error,
@@ -58,6 +58,7 @@ Error :: union {
}
General_Error :: enum {
None = 0,
File_Not_Found,
Cannot_Open_File,
File_Too_Short,
@@ -76,6 +77,7 @@ General_Error :: enum {
}
GZIP_Error :: enum {
None = 0,
Invalid_GZIP_Signature,
Reserved_Flag_Set,
Invalid_Extra_Data,
@@ -100,6 +102,7 @@ GZIP_Error :: enum {
}
ZIP_Error :: enum {
None = 0,
Invalid_ZIP_File_Signature,
Unexpected_Signature,
Insert_Next_Disk,
@@ -107,6 +110,7 @@ ZIP_Error :: enum {
}
ZLIB_Error :: enum {
None = 0,
Unsupported_Window_Size,
FDICT_Unsupported,
Unsupported_Compression_Level,
@@ -114,6 +118,7 @@ ZLIB_Error :: enum {
}
Deflate_Error :: enum {
None = 0,
Huffman_Bad_Sizes,
Huffman_Bad_Code_Lengths,
Inflate_Error,

View File

@@ -47,10 +47,10 @@ Options :: struct {
level: u8,
}
Error :: compress.Error
E_General :: compress.General_Error
E_ZLIB :: compress.ZLIB_Error
E_Deflate :: compress.Deflate_Error
Error :: compress.Error
General_Error :: compress.General_Error
ZLIB_Error :: compress.ZLIB_Error
Deflate_Error :: compress.Deflate_Error
DEFLATE_MAX_CHUNK_SIZE :: 65535
DEFLATE_MAX_LITERAL_SIZE :: 65535
@@ -258,7 +258,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
for i in 1 ..< HUFFMAN_MAX_BITS {
if sizes[i] > (1 << uint(i)) {
return E_Deflate.Huffman_Bad_Sizes
return .Huffman_Bad_Sizes
}
}
code := int(0)
@@ -270,7 +270,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
code = code + sizes[i]
if sizes[i] != 0 {
if code - 1 >= (1 << u16(i)) {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
}
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
s += 1
}
if s >= 16 {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
// code size is s, so:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
if b >= size_of(t.size) {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
if t.size[b] != s {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
compress.consume_bits_lsb(z, s)
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 {
if z.num_bits > 63 {
return 0, E_ZLIB.Code_Buffer_Malformed
return 0, .Code_Buffer_Malformed
}
compress.refill_lsb(z)
if z.num_bits > 63 {
return 0, E_General.Stream_Too_Short
return 0, .Stream_Too_Short
}
}
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if value < 256 {
e := write_byte(z, u8(value))
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
} else {
if value == 256 {
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
value, e = decode_huffman(z, z_offset)
if e != nil {
return E_Deflate.Bad_Huffman_Code
return .Bad_Huffman_Code
}
distance := Z_DIST_BASE[value]
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if z.bytes_written < i64(distance) {
// Distance is longer than we've decoded so far.
return E_Deflate.Bad_Distance
return .Bad_Distance
}
/*
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
c := z.output.buf[z.bytes_written - i64(distance)]
e := repl_byte(z, length, c)
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
}
} else {
if length > 0 {
e := repl_bytes(z, length, distance)
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
}
}
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
if !raw {
size, size_err := compress.input_size(ctx)
if size < 6 || size_err != nil {
return E_General.Stream_Too_Short
return .Stream_Too_Short
}
cmf, _ := compress.read_u8(ctx)
method := Compression_Method(cmf & 0xf)
if method != .DEFLATE {
return E_General.Unknown_Compression_Method
return .Unknown_Compression_Method
}
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
return E_ZLIB.Unsupported_Window_Size
return .Unsupported_Window_Size
}
flg, _ := compress.read_u8(ctx)
fcheck := flg & 0x1f
fcheck_computed := (cmf << 8 | flg) & 0x1f
if fcheck != fcheck_computed {
return E_General.Checksum_Failed
return .Checksum_Failed
}
/*
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
They're application specific and PNG doesn't use them.
*/
if fdict := (flg >> 5) & 1; fdict != 0 {
return E_ZLIB.FDICT_Unsupported
return .FDICT_Unsupported
}
// flevel := Compression_Level((flg >> 6) & 3);
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
output_hash := hash.adler32(ctx.output.buf[:])
if output_hash != u32(adler) {
return E_General.Checksum_Failed
return .Checksum_Failed
}
}
return nil
@@ -555,7 +555,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
if ~uncompressed_len != length_check {
return E_Deflate.Len_Nlen_Mismatch
return .Len_Nlen_Mismatch
}
/*
@@ -571,7 +571,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
assert(uncompressed_len == 0)
case 3:
return E_Deflate.BType_3
return .BType_3
case:
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
if type == 1 {
@@ -604,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
c = decode_huffman(z, codelength_ht) or_return
if c < 0 || c >= 19 {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
if c < 16 {
lencodes[n] = u8(c)
@@ -616,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
if n == 0 {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
fill = lencodes[n - 1]
case 17:
@@ -624,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
case:
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
if ntot - n < u32(c) {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
nc := n + u32(c)
@@ -639,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
}
if n != ntot {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
build_huffman(z_repeat, lencodes[:hlit]) or_return

View File

@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
if r.print_error {
fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
}
err = .Short_Read
return
}
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
if r.print_error {
fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
r.filename, count, r.offset)
}
err = .Short_Read
return
}
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data
return
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data
return
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
if len(data) < size_of(Header) {
if print_error {
fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
}
err = .Short_Read
return
}
context.allocator = allocator
header := cast(^Header)raw_data(data)
assert(header.magic_number == MAGIC_NUMBER)
if (header.magic_number != MAGIC_NUMBER) {
if print_error {
fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
}
err = .Invalid_Data
return
}
r := &Reader{
filename = filename,
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
node_count := 0
file.header = header^
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data
return

View File

@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
name_len := max(len(m.name), 255)
name_len := min(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)))
for layer in layers {
name_len := max(len(layer.name), 255)
name_len := min(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
return
}
write_value(w, &Header{
write_value(w, Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),

View File

@@ -8,17 +8,18 @@ import "core:strings"
import "core:io"
Marshal_Data_Error :: enum {
None,
Unsupported_Type,
}
Marshal_Error :: union {
Marshal_Error :: union #shared_nil {
Marshal_Data_Error,
io.Error,
}
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.make_builder(allocator)
defer if err != .None {
defer if err != nil {
strings.destroy_builder(&b)
}
@@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
return data, .None
return data, nil
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {

View File

@@ -118,7 +118,7 @@ Option :: enum {
}
Options :: distinct bit_set[Option]
Error :: union {
Error :: union #shared_nil {
General_Image_Error,
PNG_Error,
@@ -137,6 +137,7 @@ General_Image_Error :: enum {
}
PNG_Error :: enum {
None = 0,
Invalid_PNG_Signature,
IHDR_Not_First_Chunk,
IHDR_Corrupt,

View File

@@ -439,7 +439,7 @@ when false {
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
return E_PNG.Invalid_Image_Dimensions
return .Invalid_Image_Dimensions
}
mode: int = 0
@@ -450,7 +450,7 @@ when false {
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
return E_General.Cannot_Open_File
return .Cannot_Open_File
}
defer close(fd)
@@ -473,7 +473,7 @@ when false {
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
return E_PNG.Unknown_Color_Type
return .Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)

View File

@@ -62,77 +62,44 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// Atomics
atomic_fence :: proc() ---
atomic_fence_acq :: proc() ---
atomic_fence_rel :: proc() ---
atomic_fence_acqrel :: proc() ---
Atomic_Memory_Order :: enum {
Relaxed = 0, // Unordered
Consume = 1, // Monotonic
Acquire = 2,
Release = 3,
Acq_Rel = 4,
Seq_Cst = 5,
}
atomic_store :: proc(dst: ^$T, val: T) ---
atomic_store_rel :: proc(dst: ^$T, val: T) ---
atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
atomic_store_unordered :: proc(dst: ^$T, val: T) ---
atomic_thread_fence :: proc(order: Atomic_Memory_Order) ---
atomic_signal_fence :: proc(order: Atomic_Memory_Order) ---
atomic_store :: proc(dst: ^$T, val: T) ---
atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) ---
atomic_load :: proc(dst: ^$T) -> T ---
atomic_load_acq :: proc(dst: ^$T) -> T ---
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
atomic_load_unordered :: proc(dst: ^$T) -> T ---
atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
atomic_add :: proc(dst; ^$T, val: T) -> T ---
atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_and :: proc(dst; ^$T, val: T) -> T ---
atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_or :: proc(dst; ^$T, val: T) -> T ---
atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_add :: proc(dst; ^$T, val: T) -> T ---
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_and :: proc(dst; ^$T, val: T) -> T ---
atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_or :: proc(dst; ^$T, val: T) -> T ---
atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
// Constant type tests
@@ -201,6 +168,10 @@ type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, raw
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
// WASM targets only
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
wasm_memory_size :: proc(index: uintptr) -> int ---
// Internal compiler use only
__entry_point :: proc() ---

483
core/math/ease/ease.odin Normal file
View File

@@ -0,0 +1,483 @@
// easing procedures and flux easing used for animations
package ease
import "core:math"
import "core:intrinsics"
import "core:time"
@(private) PI_2 :: math.PI / 2
// converted to odin from https://github.com/warrenm/AHEasing
// with additional enum based call
// Modeled after the parabola y = x^2
quadratic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p
}
// Modeled after the parabola y = -x^2 + 2x
quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return -(p * (p - 2))
}
// Modeled after the piecewise quadratic
// y = (1/2)((2x)^2) ; [0, 0.5)
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 2 * p * p
} else {
return (-2 * p * p) + (4 * p) - 1
}
}
// Modeled after the cubic y = x^3
cubic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p
}
// Modeled after the cubic y = (x - 1)^3 + 1
cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f + 1
}
// Modeled after the piecewise cubic
// y = (1/2)((2x)^3) ; [0, 0.5)
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 4 * p * p * p
} else {
f := (2 * p) - 2
return 0.5 * f * f * f + 1
}
}
// Modeled after the quartic x^4
quartic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p * p
}
// Modeled after the quartic y = 1 - (x - 1)^4
quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f * (1 - p) + 1
}
// Modeled after the piecewise quartic
// y = (1/2)((2x)^4) ; [0, 0.5)
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 8 * p * p * p * p
} else {
f := p - 1
return -8 * f * f * f * f + 1
}
}
// Modeled after the quintic y = x^5
quintic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p * p * p
}
// Modeled after the quintic y = (x - 1)^5 + 1
quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f * f * f + 1
}
// Modeled after the piecewise quintic
// y = (1/2)((2x)^5) ; [0, 0.5)
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 16 * p * p * p * p * p
} else {
f := (2 * p) - 2
return 0.5 * f * f * f * f * f + 1
}
}
// Modeled after quarter-cycle of sine wave
sine_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin((p - 1) * PI_2) + 1
}
// Modeled after quarter-cycle of sine wave (different phase)
sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(p * PI_2)
}
// Modeled after half sine wave
sine_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 0.5 * (1 - math.cos(p * math.PI))
}
// Modeled after shifted quadrant IV of unit circle
circular_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 1 - math.sqrt(1 - (p * p))
}
// Modeled after shifted quadrant II of unit circle
circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sqrt((2 - p) * p)
}
// Modeled after the piecewise circular function
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * (1 - math.sqrt(1 - 4 * (p * p)))
} else {
return 0.5 * (math.sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1)
}
}
// Modeled after the exponential function y = 2^(10(x - 1))
exponential_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p == 0.0 ? p : math.pow(2, 10 * (p - 1))
}
// Modeled after the exponential function y = -2^(-10x) + 1
exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p == 1.0 ? p : 1 - math.pow(2, -10 * p)
}
// Modeled after the piecewise exponential
// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p == 0.0 || p == 1.0 {
return p
}
if p < 0.5 {
return 0.5 * math.pow(2, (20 * p) - 10)
} else {
return -0.5 * math.pow(2, (-20 * p) + 10) + 1
}
}
// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
elastic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(13 * PI_2 * p) * math.pow(2, 10 * (p - 1))
}
// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(-13 * PI_2 * (p + 1)) * math.pow(2, -10 * p) + 1
}
// Modeled after the piecewise exponentially-damped sine wave:
// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * math.sin(13 * PI_2 * (2 * p)) * math.pow(2, 10 * ((2 * p) - 1))
} else {
return 0.5 * (math.sin(-13 * PI_2 * ((2 * p - 1) + 1)) * math.pow(2, -10 * (2 * p - 1)) + 2)
}
}
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
back_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p - p * math.sin(p * math.PI)
}
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := 1 - p
return 1 - (f * f * f - f * math.sin(f * math.PI))
}
// Modeled after the piecewise overshooting cubic function:
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
f := 2 * p
return 0.5 * (f * f * f - f * math.sin(f * math.PI))
} else {
f := (1 - (2*p - 1))
return 0.5 * (1 - (f * f * f - f * math.sin(f * math.PI))) + 0.5
}
}
bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 1 - bounce_out(1 - p)
}
bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 4/11.0 {
return (121 * p * p)/16.0
} else if p < 8/11.0 {
return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0
} else if p < 9/10.0 {
return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0
} else {
return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0
}
}
bounce_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * bounce_in(p*2)
} else {
return 0.5 * bounce_out(p * 2 - 1) + 0.5
}
}
// additional enum variant
Ease :: enum {
Linear,
Quadratic_In,
Quadratic_Out,
Quadratic_In_Out,
Cubic_In,
Cubic_Out,
Cubic_In_Out,
Quartic_In,
Quartic_Out,
Quartic_In_Out,
Quintic_In,
Quintic_Out,
Quintic_In_Out,
Sine_In,
Sine_Out,
Sine_In_Out,
Circular_In,
Circular_Out,
Circular_In_Out,
Exponential_In,
Exponential_Out,
Exponential_In_Out,
Elastic_In,
Elastic_Out,
Elastic_In_Out,
Back_In,
Back_Out,
Back_In_Out,
Bounce_In,
Bounce_Out,
Bounce_In_Out,
}
ease :: proc "contextless" (type: Ease, p: $T) -> T
where intrinsics.type_is_float(T) {
switch type {
case .Linear: return p
case .Quadratic_In: return quadratic_in(p)
case .Quadratic_Out: return quadratic_out(p)
case .Quadratic_In_Out: return quadratic_in_out(p)
case .Cubic_In: return cubic_in(p)
case .Cubic_Out: return cubic_out(p)
case .Cubic_In_Out: return cubic_in_out(p)
case .Quartic_In: return quartic_in(p)
case .Quartic_Out: return quartic_out(p)
case .Quartic_In_Out: return quartic_in_out(p)
case .Quintic_In: return quintic_in(p)
case .Quintic_Out: return quintic_out(p)
case .Quintic_In_Out: return quintic_in_out(p)
case .Sine_In: return sine_in(p)
case .Sine_Out: return sine_out(p)
case .Sine_In_Out: return sine_in_out(p)
case .Circular_In: return circular_in(p)
case .Circular_Out: return circular_out(p)
case .Circular_In_Out: return circular_in_out(p)
case .Exponential_In: return exponential_in(p)
case .Exponential_Out: return exponential_out(p)
case .Exponential_In_Out: return exponential_in_out(p)
case .Elastic_In: return elastic_in(p)
case .Elastic_Out: return elastic_out(p)
case .Elastic_In_Out: return elastic_in_out(p)
case .Back_In: return back_in(p)
case .Back_Out: return back_out(p)
case .Back_In_Out: return back_in_out(p)
case .Bounce_In: return bounce_in(p)
case .Bounce_Out: return bounce_out(p)
case .Bounce_In_Out: return bounce_in_out(p)
}
// in case type was invalid
return 0
}
Flux_Map :: struct($T: typeid) {
values: map[^T]Flux_Tween(T),
}
Flux_Tween :: struct($T: typeid) {
value: ^T,
start: T,
diff: T,
goal: T,
delay: f64, // in seconds
duration: time.Duration,
progress: f64,
rate: f64,
type: Ease,
inited: bool,
// callbacks, data can be set, will be pushed to callback
data: rawptr, // by default gets set to value input
on_start: proc(flux: ^Flux_Map(T), data: rawptr),
on_update: proc(flux: ^Flux_Map(T), data: rawptr),
on_complete: proc(flux: ^Flux_Map(T), data: rawptr),
}
// init flux map to a float type and a wanted cap
flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
return {
make(map[^T]Flux_Tween(T), cap),
}
}
// delete map content
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
delete(flux.values)
}
// clear map content, stops all animations
flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
clear(&flux.values)
}
// append / overwrite existing tween value to parameters
// rest is initialized in flux_tween_init, inside update
// return value can be used to set callbacks
flux_to :: proc(
flux: ^Flux_Map($T),
value: ^f32,
goal: f32,
type: Ease = .Quadratic_Out,
duration: time.Duration = time.Second,
delay: f64 = 0,
) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) {
if res, ok := &flux.values[value]; ok {
tween = res
} else {
flux.values[value] = {}
tween = &flux.values[value]
}
tween^ = {
value = value,
goal = goal,
duration = duration,
delay = delay,
type = type,
data = value,
}
return
}
// init internal properties
flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) {
tween.inited = true
tween.start = tween.value^
tween.diff = tween.goal - tween.value^
s := time.duration_seconds(duration)
tween.rate = duration > 0 ? 1.0 / s : 0
tween.progress = duration > 0 ? 0 : 1
}
// update all tweens, wait for their delay if one exists
// calls callbacks in all stages, when they're filled
// deletes tween from the map after completion
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
for key, tween in &flux.values {
delay_remainder := f64(0)
// Update delay if necessary.
if tween.delay > 0 {
tween.delay -= dt
if tween.delay < 0 {
// We finished the delay, but in doing so consumed part of this frame's `dt` budget.
// Keep track of it so we can apply it to this tween without affecting others.
delay_remainder = tween.delay
// We're done with this delay.
tween.delay = 0
}
}
// We either had no delay, or the delay has been consumed.
if tween.delay <= 0 {
if !tween.inited {
flux_tween_init(&tween, tween.duration)
if tween.on_start != nil {
tween.on_start(flux, tween.data)
}
}
// If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
// that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
// to the tween so it advances properly, instead of too much or little.
tween.progress += tween.rate * (dt + delay_remainder)
x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
tween.value^ = tween.start + tween.diff * T(x)
if tween.on_update != nil {
tween.on_update(flux, tween.data)
}
if tween.progress >= 1 {
delete_key(&flux.values, key)
if tween.on_complete != nil {
tween.on_complete(flux, tween.data)
}
}
}
}
}
// stop a specific key inside the map
// returns true when it successfully removed the key
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
if key in flux.values {
delete_key(&flux.values, key)
return true
}
return false
}
// returns the amount of time left for the tween animation, if the key exists in the map
// returns 0 if the tween doesnt exist on the map
flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 {
if tween, ok := flux.values[key]; ok {
return ((1 - tween.progress) * tween.rate) + tween.delay
} else {
return 0
}
}

View File

@@ -1206,7 +1206,7 @@ prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
return
}
cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T
cumsum_inplace :: proc "contextless" (x: $T/[]$E)
where intrinsics.type_is_numeric(E) {
for i in 1..<len(x) {
x[i] = x[i-1] + x[i]
@@ -1736,4 +1736,4 @@ INF_F64 :f64: 0h7FF0_0000_0000_0000
NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
SNAN_F64 :f64: 0h7FF0_0000_0000_0001
QNAN_F64 :f64: 0h7FF8_0000_0000_0001
QNAN_F64 :f64: 0h7FF8_0000_0000_0001

View File

@@ -6,7 +6,24 @@ import "core:runtime"
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
return nil, nil
switch mode {
case .Alloc:
return nil, .Out_Of_Memory
case .Free:
return nil, .None
case .Free_All:
return nil, .Mode_Not_Implemented
case .Resize:
if size == 0 {
return nil, .None
}
return nil, .Out_Of_Memory
case .Query_Features:
return nil, .Mode_Not_Implemented
case .Query_Info:
return nil, .Mode_Not_Implemented
}
return nil, .None
}
nil_allocator :: proc() -> Allocator {

View File

@@ -16,7 +16,7 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
// memset_s call.
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
intrinsics.atomic_fence() // Prevent reordering
intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
zero_item :: proc "contextless" (item: $P/^$T) {

View File

@@ -1,7 +1,7 @@
//+private
package mem_virtual
import sync "core:sync/sync2"
import "core:sync"
Platform_Memory_Block :: struct {
block: Memory_Block,

View File

@@ -708,13 +708,19 @@ Struct_Type :: struct {
name_count: int,
}
Union_Type_Kind :: enum u8 {
Normal,
maybe,
no_nil,
shared_nil,
}
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
is_maybe: bool,
is_no_nil: bool,
kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,

View File

@@ -2630,8 +2630,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
is_maybe: bool
is_no_nil: bool
is_maybe: bool
is_no_nil: bool
is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2663,12 +2664,34 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_no_nil = true
case "shared_nil":
if is_shared_nil {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_shared_nil = true
case:
error(p, tag.pos, "invalid union tag '#%s", tag.text)
}
}
p.expr_level = prev_level
if is_no_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
}
if is_no_nil && is_shared_nil {
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
}
if is_shared_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
}
union_kind := ast.Union_Type_Kind.Normal
switch {
case is_maybe: union_kind = .maybe
case is_no_nil: union_kind = .no_nil
case is_shared_nil: union_kind = .shared_nil
}
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2699,14 +2722,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close))
ut.poly_params = poly_params
ut.variants = variants[:]
ut.align = align
ut.where_token = where_token
ut.where_clauses = where_clauses
ut.is_maybe = is_maybe
ut.is_no_nil = is_no_nil
ut.kind = union_kind
return ut

View File

@@ -1046,8 +1046,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_poly_params(p, v.poly_params)
if v.is_maybe {
push_ident_token(p, "#maybe", 1)
switch v.kind {
case .Normal:
case .maybe: push_ident_token(p, "#maybe", 1)
case .no_nil: push_ident_token(p, "#no_nil", 1)
case .shared_nil: push_ident_token(p, "#shared_nil", 1)
}
push_where_clauses(p, v.where_clauses)

View File

@@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)

View File

@@ -1,6 +1,6 @@
package os2
import sync "core:sync/sync2"
import "core:sync"
import "core:time"
import "core:runtime"

View File

@@ -2,7 +2,6 @@ package os
import "core:time"
File_Info :: struct {
fullpath: string,
name: string,

View File

@@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno)
return _stat(name, attrs, allocator)
}
fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) {
if fd == 0 {
return {}, ERROR_INVALID_HANDLE
}
@@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno)
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi: File_Info
fi.fullpath = path
fi.name = basename(path)
fi.mode |= file_type_mode(h)
return fi, ERROR_NONE
errno = ERROR_NONE
case:
fi, errno = file_info_from_get_file_information_by_handle(path, h)
}
return file_info_from_get_file_information_by_handle(path, h)
fi.fullpath = path
return
}

View File

@@ -220,9 +220,11 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
context.allocator = allocator
if !has_meta(pattern) {
// TODO(bill): os.lstat on here to check for error
m := make([]string, 1, allocator)
m := make([]string, 1)
m[0] = pattern
return m[:], .None
}
@@ -232,6 +234,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
when ODIN_OS == .Windows {
temp_buf: [8]byte
volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
} else {
dir = clean_glob_path(dir)
}
@@ -246,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
dmatches := make([dynamic]string, 0, 0, allocator)
dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
if err != .None {
@@ -258,11 +261,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) {
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
context.allocator = allocator
if matches != nil {
m = matches^
} else {
m = make([dynamic]string, 0, 0, context.allocator)
m = make([dynamic]string, 0, 0)
}
@@ -275,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
{
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
if ferr != 0 {
return
}

View File

@@ -1,5 +1,5 @@
// The path/filepath package uses either forward slashes or backslashes depending on the operating system
// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:strings"
@@ -122,6 +122,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
vol_and_path = original_path,
vol_len = vol_len,
}
defer lazy_buffer_destroy(out)
r, dot_dot := 0, 0
if rooted {
@@ -170,7 +171,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
cleaned, new_allocation := from_slash(s)
if new_allocation {
delete(s)
lazy_buffer_destroy(out)
}
return cleaned
}
@@ -300,6 +300,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
// For Windows the separator is `;`, for Unix it's `:`.
// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> []string {
if path == "" {
return nil
@@ -322,7 +327,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
start, quote = 0, false
list := make([]string, count, allocator)
list := make([]string, count + 1, allocator)
index := 0
for i := 0; i < len(path); i += 1 {
c := path[i]
@@ -336,6 +341,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
}
assert(index == count)
list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)

View File

@@ -1054,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
case f16: value = u64(v)
case f32: value = u64(v)
case f64: value = u64(v)
case f32le: value = u64(v)
@@ -1159,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
case f16: value = f64(v)
case f32: value = f64(v)
case f64: value = (v)
case f32le: value = f64(v)

View File

@@ -136,6 +136,7 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
maybe: bool,
shared_nil: bool,
}
Type_Info_Enum :: struct {
base: ^Type_Info,

View File

@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
#force_no_inline _cleanup_runtime()
return 0
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,167 +2,44 @@ package sync
import "core:intrinsics"
Ordering :: enum {
Relaxed, // Monotonic
Release,
Acquire,
Acquire_Release,
Sequentially_Consistent,
}
strongest_failure_ordering_table := [Ordering]Ordering{
.Relaxed = .Relaxed,
.Release = .Relaxed,
.Acquire = .Acquire,
.Acquire_Release = .Acquire,
.Sequentially_Consistent = .Sequentially_Consistent,
}
strongest_failure_ordering :: #force_inline proc(order: Ordering) -> Ordering {
return strongest_failure_ordering_table[order]
}
fence :: #force_inline proc($order: Ordering) {
when order == .Relaxed { #panic("there is no such thing as a relaxed fence") }
else when order == .Release { intrinsics.atomic_fence_rel() }
else when order == .Acquire { intrinsics.atomic_fence_acq() }
else when order == .Acquire_Release { intrinsics.atomic_fence_acqrel() }
else when order == .Sequentially_Consistent { intrinsics.atomic_fence() }
else { #panic("unknown order") }
cpu_relax :: intrinsics.cpu_relax
/*
Atomic_Memory_Order :: enum {
Relaxed = 0,
Consume = 1,
Acquire = 2,
Release = 3,
Acq_Rel = 4,
Seq_Cst = 5,
}
*/
Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) {
when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) }
else when order == .Release { intrinsics.atomic_store_rel(dst, val) }
else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) }
else when order == .Acquire { #panic("there is not such thing as an acquire store") }
else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") }
else { #panic("unknown order") }
}
atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) }
else when order == .Acquire { return intrinsics.atomic_load_acq(dst) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) }
else when order == .Release { #panic("there is no such thing as a release load") }
else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") }
else { #panic("unknown order") }
}
atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) }
else { #panic("unknown order") }
}
atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
when failure == .Relaxed {
when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) }
else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) }
else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) }
else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) }
else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Acquire {
when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) }
else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Sequentially_Consistent {
when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Acquire_Release {
#panic("there is not such thing as an acquire/release failure ordering")
} else when failure == .Release {
when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else {
return T{}, false
}
}
atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
when failure == .Relaxed {
when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) }
else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) }
else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) }
else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) }
else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Acquire {
when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) }
else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Sequentially_Consistent {
when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else when failure == .Acquire_Release {
#panic("there is not such thing as an acquire/release failure ordering")
} else when failure == .Release {
when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) }
else { #panic("an unknown ordering combination") }
} else {
return T{}, false
}
}
atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_add_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) }
else { #panic("unknown order") }
}
atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) }
else { #panic("unknown order") }
}
atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_and_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) }
else { #panic("unknown order") }
}
atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) }
else { #panic("unknown order") }
}
atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_or_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) }
else { #panic("unknown order") }
}
atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) }
else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) }
else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) }
else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) }
else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) }
else { #panic("unknown order") }
}
atomic_thread_fence :: intrinsics.atomic_thread_fence
atomic_signal_fence :: intrinsics.atomic_signal_fence
atomic_store :: intrinsics.atomic_store
atomic_store_explicit :: intrinsics.atomic_store_explicit
atomic_load :: intrinsics.atomic_load
atomic_load_explicit :: intrinsics.atomic_load_explicit
atomic_add :: intrinsics.atomic_add
atomic_add_explicit :: intrinsics.atomic_add_explicit
atomic_sub :: intrinsics.atomic_sub
atomic_sub_explicit :: intrinsics.atomic_sub_explicit
atomic_and :: intrinsics.atomic_and
atomic_and_explicit :: intrinsics.atomic_and_explicit
atomic_nand :: intrinsics.atomic_nand
atomic_nand_explicit :: intrinsics.atomic_nand_explicit
atomic_or :: intrinsics.atomic_or
atomic_or_explicit :: intrinsics.atomic_or_explicit
atomic_xor :: intrinsics.atomic_xor
atomic_xor_explicit :: intrinsics.atomic_xor_explicit
atomic_exchange :: intrinsics.atomic_exchange
atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
// Returns value and optional ok boolean
atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit

View File

@@ -1,80 +0,0 @@
package sync
/*
A barrier enabling multiple threads to synchronize the beginning of some computation
Example:
package example
import "core:fmt"
import "core:sync"
import "core:thread"
barrier := &sync.Barrier{};
main :: proc() {
fmt.println("Start");
THREAD_COUNT :: 4;
threads: [THREAD_COUNT]^thread.Thread;
sync.barrier_init(barrier, THREAD_COUNT);
defer sync.barrier_destroy(barrier);
for _, i in threads {
threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
// Same messages will be printed together but without any interleaving
fmt.println("Getting ready!");
sync.barrier_wait(barrier);
fmt.println("Off their marks they go!");
});
}
for t in threads {
thread.destroy(t); // join and free thread
}
fmt.println("Finished");
}
*/
Barrier :: struct {
mutex: Blocking_Mutex,
cond: Condition,
index: int,
generation_id: int,
thread_count: int,
}
barrier_init :: proc(b: ^Barrier, thread_count: int) {
blocking_mutex_init(&b.mutex)
condition_init(&b.cond, &b.mutex)
b.index = 0
b.generation_id = 0
b.thread_count = thread_count
}
barrier_destroy :: proc(b: ^Barrier) {
blocking_mutex_destroy(&b.mutex)
condition_destroy(&b.cond)
}
// Block the current thread until all threads have rendezvoused
// Barrier can be reused after all threads rendezvoused once, and can be used continuously
barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
blocking_mutex_lock(&b.mutex)
defer blocking_mutex_unlock(&b.mutex)
local_gen := b.generation_id
b.index += 1
if b.index < b.thread_count {
for local_gen == b.generation_id && b.index < b.thread_count {
condition_wait_for(&b.cond)
}
return false
}
b.index = 0
b.generation_id += 1
condition_broadcast(&b.cond)
return true
}

View File

@@ -1,889 +0,0 @@
package sync
import "core:mem"
import "core:time"
import "core:intrinsics"
import "core:math/rand"
_, _ :: time, rand
Channel_Direction :: enum i8 {
Both = 0,
Send = +1,
Recv = -1,
}
Channel :: struct($T: typeid, $Direction := Channel_Direction.Both) {
using _internal: ^Raw_Channel,
}
channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
context.allocator = allocator
ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
return
}
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
context.allocator = allocator
ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
return
}
channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
context.allocator = allocator
ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
return
}
channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
context.allocator = allocator
ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
return
}
channel_destroy :: proc(ch: $C/Channel($T, $D)) {
raw_channel_destroy(ch._internal)
}
channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
res._internal = ch._internal
return
}
channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
res._internal = ch._internal
return
}
channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.len if ch._internal != nil else 0
}
channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.cap if ch._internal != nil else 0
}
channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
msg := msg
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc)
}
channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
msg := msg
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc)
}
channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
c := ch._internal
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc)
}
mutex_lock(&c.mutex)
raw_channel_recv_impl(c, &msg, loc)
mutex_unlock(&c.mutex)
return
}
channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal
if c != nil && mutex_try_lock(&c.mutex) {
if c.len > 0 {
raw_channel_recv_impl(c, &msg, loc)
ok = true
}
mutex_unlock(&c.mutex)
}
return
}
channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
res: T
res, ok = channel_try_recv(ch, loc)
if ok && msg != nil {
msg^ = res
}
return
}
channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
return ch._internal == nil
}
channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
c := ch._internal
return c != nil && !c.closed
}
channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal == b._internal
}
channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal != b._internal
}
channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
return raw_channel_can_send(ch._internal)
}
channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
return raw_channel_can_recv(ch._internal)
}
channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
c := ch._internal
if c == nil {
return -1
}
if intrinsics.atomic_load(&c.closed) {
return -1
}
return intrinsics.atomic_load(&c.len)
}
channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
raw_channel_close(ch._internal, loc)
}
channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal
if c == nil {
return
}
if !c.closed || c.len > 0 {
msg, ok = channel_recv(ch), true
}
return
}
channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
raw_channel_drain(ch._internal)
}
channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
for msg in channel_iterator(src) {
channel_send(dst, msg)
}
}
Raw_Channel_Wait_Queue :: struct {
next: ^Raw_Channel_Wait_Queue,
state: ^uintptr,
}
Raw_Channel :: struct {
closed: bool,
ready: bool, // ready to recv
data_offset: u16, // data is stored at the end of this data structure
elem_size: u32,
len, cap: int,
read, write: int,
mutex: Mutex,
cond: Condition,
allocator: mem.Allocator,
sendq: ^Raw_Channel_Wait_Queue,
recvq: ^Raw_Channel_Wait_Queue,
}
raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
val.next = head^
head^ = val
}
raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
p := head
for p^ != nil && p^ != val {
p = &p^.next
}
if p != nil {
p^ = p^.next
}
}
raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
assert(int(u32(elem_size)) == elem_size)
s := size_of(Raw_Channel)
s = mem.align_forward_int(s, elem_align)
data_offset := uintptr(s)
s += elem_size * max(cap, 1)
a := max(elem_align, align_of(Raw_Channel))
c := (^Raw_Channel)(mem.alloc(s, a))
if c == nil {
return nil
}
c.data_offset = u16(data_offset)
c.elem_size = u32(elem_size)
c.len, c.cap = 0, max(cap, 0)
c.read, c.write = 0, 0
mutex_init(&c.mutex)
condition_init(&c.cond, &c.mutex)
c.allocator = context.allocator
c.closed = false
return c
}
raw_channel_destroy :: proc(c: ^Raw_Channel) {
if c == nil {
return
}
context.allocator = c.allocator
intrinsics.atomic_store(&c.closed, true)
condition_destroy(&c.cond)
mutex_destroy(&c.mutex)
free(c)
}
raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
if c == nil {
panic(message="cannot close nil channel", loc=loc)
}
mutex_lock(&c.mutex)
defer mutex_unlock(&c.mutex)
intrinsics.atomic_store(&c.closed, true)
// Release readers and writers
raw_channel_wait_queue_broadcast(c.recvq)
raw_channel_wait_queue_broadcast(c.sendq)
condition_broadcast(&c.cond)
}
raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
send :: proc(c: ^Raw_Channel, src: rawptr) {
data := uintptr(c) + uintptr(c.data_offset)
dst := data + uintptr(c.write * int(c.elem_size))
mem.copy(rawptr(dst), src, int(c.elem_size))
c.len += 1
c.write = (c.write + 1) % max(c.cap, 1)
}
switch {
case c == nil:
panic(message="cannot send message; channel is nil", loc=loc)
case c.closed:
panic(message="cannot send message; channel is closed", loc=loc)
}
mutex_lock(&c.mutex)
defer mutex_unlock(&c.mutex)
if c.cap > 0 {
if !block && c.len >= c.cap {
return false
}
for c.len >= c.cap {
condition_wait_for(&c.cond)
}
} else if c.len > 0 { // TODO(bill): determine correct behaviour
if !block {
return false
}
condition_wait_for(&c.cond)
} else if c.len == 0 && !block {
return false
}
send(c, msg)
condition_signal(&c.cond)
raw_channel_wait_queue_signal(c.recvq)
return true
}
raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
if c.len < 1 {
panic(message="cannot recv message; channel is empty", loc=loc)
}
c.len -= 1
data := uintptr(c) + uintptr(c.data_offset)
src := data + uintptr(c.read * int(c.elem_size))
mem.copy(dst, rawptr(src), int(c.elem_size))
c.read = (c.read + 1) % max(c.cap, 1)
}
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc)
}
intrinsics.atomic_store(&c.ready, true)
for c.len < 1 {
raw_channel_wait_queue_signal(c.sendq)
condition_wait_for(&c.cond)
}
intrinsics.atomic_store(&c.ready, false)
recv(c, res, loc)
if c.cap > 0 {
if c.len == c.cap - 1 {
// NOTE(bill): Only signal on the last one
condition_signal(&c.cond)
}
} else {
condition_signal(&c.cond)
}
}
raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false
}
mutex_lock(&c.mutex)
switch {
case c.closed:
ok = false
case c.cap > 0:
ok = c.ready && c.len < c.cap
case:
ok = c.ready && c.len == 0
}
mutex_unlock(&c.mutex)
return
}
raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false
}
mutex_lock(&c.mutex)
ok = c.len > 0
mutex_unlock(&c.mutex)
return
}
raw_channel_drain :: proc(c: ^Raw_Channel) {
if c == nil {
return
}
mutex_lock(&c.mutex)
c.len = 0
c.read = 0
c.write = 0
mutex_unlock(&c.mutex)
}
MAX_SELECT_CHANNELS :: 64
SELECT_MAX_TIMEOUT :: max(time.Duration)
Select_Command :: enum {
Recv,
Send,
}
Select_Channel :: struct {
channel: ^Raw_Channel,
command: Select_Command,
}
select :: proc(channels: ..Select_Channel) -> (index: int) {
return select_timeout(SELECT_MAX_TIMEOUT, ..channels)
}
select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
backing: [MAX_SELECT_CHANNELS]int
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
candidates := backing[:]
cap := len(channels)
candidates = candidates[:cap]
count := u32(0)
for c, i in channels {
if c.channel == nil {
continue
}
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i
count += 1
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i
count += 1
}
}
}
if count == 0 {
wait_state: uintptr = 0
for _, i in channels {
q := &queues[i]
q.state = &wait_state
}
for c, i in channels {
if c.channel == nil {
continue
}
q := &queues[i]
switch c.command {
case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q)
case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q)
}
}
raw_channel_wait_queue_wait_on(&wait_state, timeout)
for c, i in channels {
if c.channel == nil {
continue
}
q := &queues[i]
switch c.command {
case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q)
case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q)
}
}
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i
count += 1
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i
count += 1
}
}
}
if count == 0 && timeout == SELECT_MAX_TIMEOUT {
index = -1
return
}
assert(count != 0)
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
backing: [MAX_SELECT_CHANNELS]int
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
candidates := backing[:]
cap := len(channels)
candidates = candidates[:cap]
count := u32(0)
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
state: uintptr
for c, i in channels {
q := &queues[i]
q.state = &state
raw_channel_wait_queue_insert(&c.recvq, q)
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
for c, i in channels {
q := &queues[i]
raw_channel_wait_queue_remove(&c.recvq, q)
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
assert(count != 0)
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
candidates: [MAX_SELECT_CHANNELS]int
count := u32(0)
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
state: uintptr
for c, i in channels {
q := &queues[i]
q.state = &state
raw_channel_wait_queue_insert(&c.recvq, q)
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
for c, i in channels {
q := &queues[i]
raw_channel_wait_queue_remove(&c.recvq, q)
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
assert(count != 0)
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
msg = channel_recv(channels[index])
return
}
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
backing: [MAX_SELECT_CHANNELS]int
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
candidates := backing[:]
cap := len(channels)
candidates = candidates[:cap]
count := u32(0)
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
state: uintptr
for c, i in channels {
q := &queues[i]
q.state = &state
raw_channel_wait_queue_insert(&c.recvq, q)
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
for c, i in channels {
q := &queues[i]
raw_channel_wait_queue_remove(&c.recvq, q)
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
assert(count != 0)
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
if msg != nil {
channel_send(channels[index], msg)
}
return
}
select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
candidates: [MAX_SELECT_CHANNELS]int
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
count := u32(0)
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
state: uintptr
for c, i in channels {
q := &queues[i]
q.state = &state
raw_channel_wait_queue_insert(&c.sendq, q)
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
for c, i in channels {
q := &queues[i]
raw_channel_wait_queue_remove(&c.sendq, q)
}
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i
count += 1
}
}
assert(count != 0)
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_try :: proc(channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels")
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
backing: [MAX_SELECT_CHANNELS]int
candidates := backing[:]
cap := len(channels)
candidates = candidates[:cap]
count := u32(0)
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i
count += 1
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i
count += 1
}
}
}
if count == 0 {
index = -1
return
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
index = -1
return
case 1:
index = -1
if raw_channel_can_recv(channels[0]) {
index = 0
}
return
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
candidates: [MAX_SELECT_CHANNELS]int
count := u32(0)
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
index = -1
return
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
switch len(channels) {
case 0:
return -1
case 1:
if raw_channel_can_send(channels[0]) {
return 0
}
return -1
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
candidates: [MAX_SELECT_CHANNELS]int
count := u32(0)
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
index = -1
return
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
return
}
select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
index = -1
return
case 1:
ok: bool
if msg, ok = channel_try_recv(channels[0]); ok {
index = 0
}
return
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
candidates: [MAX_SELECT_CHANNELS]int
count := u32(0)
for c, i in channels {
if channel_can_recv(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
index = -1
return
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
msg = channel_recv(channels[index])
return
}
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
index = -1
switch len(channels) {
case 0:
return
case 1:
if channel_try_send(channels[0], msg) {
index = 0
}
return
}
assert(len(channels) <= MAX_SELECT_CHANNELS)
candidates: [MAX_SELECT_CHANNELS]int
count := u32(0)
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i
count += 1
}
}
if count == 0 {
index = -1
return
}
t := time.now()
r := rand.create(transmute(u64)t)
i := rand.uint32(&r)
index = candidates[i % count]
channel_send(channels[index], msg)
return
}

View File

@@ -1,16 +0,0 @@
// +build linux, darwin, freebsd, openbsd
package sync
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
// stub
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}

View File

@@ -1,33 +0,0 @@
package sync
import "core:intrinsics"
import win32 "core:sys/windows"
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
ms: win32.DWORD = win32.INFINITE
if max(time.Duration) != SELECT_MAX_TIMEOUT {
ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000)
}
v := intrinsics.atomic_load(state)
for v == 0 {
win32.WaitOnAddress(state, &v, size_of(state^), ms)
v = intrinsics.atomic_load(state)
}
intrinsics.atomic_store(state, 0)
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
intrinsics.atomic_add(x.state, 1)
win32.WakeByAddressSingle(x.state)
}
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
intrinsics.atomic_add(x.state, 1)
win32.WakeByAddressAll(x.state)
}
}

View File

@@ -1,4 +1,4 @@
package sync2
package sync
import "core:time"
@@ -146,10 +146,10 @@ Auto_Reset_Event :: struct {
}
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
old_status := atomic_load_relaxed(&e.status)
old_status := atomic_load_explicit(&e.status, .Relaxed)
for {
new_status := old_status + 1 if old_status < 1 else 1
if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok {
if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok {
break
}
@@ -160,7 +160,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
}
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
old_status := atomic_sub_acquire(&e.status, 1)
old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 {
sema_wait(&e.sema)
}
@@ -174,14 +174,14 @@ Ticket_Mutex :: struct {
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add_relaxed(&m.ticket, 1)
for ticket != atomic_load_acquire(&m.serving) {
ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
for ticket != atomic_load_explicit(&m.serving, .Acquire) {
cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
atomic_add_relaxed(&m.serving, 1)
atomic_add_explicit(&m.serving, 1, .Relaxed)
}
@(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
@@ -196,18 +196,18 @@ Benaphore :: struct {
}
benaphore_lock :: proc(b: ^Benaphore) {
if atomic_add_acquire(&b.counter, 1) > 1 {
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0)
v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
if atomic_sub_release(&b.counter, 1) > 0 {
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema)
}
}
@@ -227,7 +227,7 @@ Recursive_Benaphore :: struct {
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
if atomic_add_acquire(&b.counter, 1) > 1 {
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
if tid != b.owner {
sema_wait(&b.sema)
}
@@ -240,10 +240,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
atomic_add_acquire(&b.counter, 1)
atomic_add_explicit(&b.counter, 1, .Acquire)
}
if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 {
if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire); v != 0 {
return false
}
// inside the lock
@@ -260,7 +260,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
if recursion == 0 {
b.owner = 0
}
if atomic_sub_release(&b.counter, 1) > 0 {
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
if recursion == 0 {
sema_post(&b.sema)
}
@@ -293,12 +293,12 @@ once_do :: proc(o: ^Once, fn: proc()) {
defer mutex_unlock(&o.m)
if !o.done {
fn()
atomic_store_release(&o.done, true)
atomic_store_explicit(&o.done, true, .Release)
}
}
if atomic_load_acquire(&o.done) == false {
if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}

View File

@@ -1,6 +1,6 @@
//+private
//+build darwin
package sync2
package sync
import "core:c"
import "core:time"

View File

@@ -1,6 +1,6 @@
//+private
//+build freebsd
package sync2
package sync
import "core:c"
import "core:os"

View File

@@ -1,6 +1,6 @@
//+private
//+build linux
package sync2
package sync
import "core:c"
import "core:time"
@@ -14,12 +14,6 @@ FUTEX_PRIVATE_FLAG :: 128
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
foreign import libc "system:c"
foreign libc {
__errno_location :: proc "c" () -> ^c.int ---
}
ESUCCESS :: 0
EINTR :: -4
EAGAIN :: -11

View File

@@ -1,6 +1,6 @@
//+private
//+build openbsd
package sync2
package sync
import "core:c"
import "core:os"

View File

@@ -1,6 +1,6 @@
//+private
//+build windows
package sync2
package sync
import "core:time"

View File

@@ -1,4 +1,4 @@
package sync2
package sync
import "core:time"

View File

@@ -1,4 +1,4 @@
package sync2
package sync
import "core:time"
@@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
new_state := curr_state // Make a copy of it
spin_lock: for spin in 0..<i32(100) {
state, ok := atomic_compare_exchange_weak_acquire(&m.state, .Unlocked, new_state)
state, ok := atomic_compare_exchange_weak_explicit(&m.state, .Unlocked, new_state, .Acquire, .Consume)
if ok {
return
}
@@ -38,8 +38,11 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
}
}
// Set just in case 100 iterations did not do it
new_state = .Waiting
for {
if atomic_exchange_acquire(&m.state, .Waiting) == .Unlocked {
if atomic_exchange_explicit(&m.state, .Waiting, .Acquire) == .Unlocked {
return
}
@@ -49,11 +52,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
}
switch v := atomic_exchange_acquire(&m.state, .Locked); v {
case .Unlocked:
// Okay
case: fallthrough
case .Locked, .Waiting:
if v := atomic_exchange_explicit(&m.state, .Locked, .Acquire); v != .Unlocked {
lock_slow(m, v)
}
}
@@ -66,7 +65,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
}
switch atomic_exchange_release(&m.state, .Unlocked) {
switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
case .Unlocked:
unreachable()
case .Locked:
@@ -78,7 +77,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
// atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
_, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked)
_, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
return ok
}
@@ -291,7 +290,7 @@ Queue_Item :: struct {
@(private="file")
queue_item_wait :: proc(item: ^Queue_Item) {
for atomic_load_acquire(&item.futex) == 0 {
for atomic_load_explicit(&item.futex, .Acquire) == 0 {
futex_wait(&item.futex, 0)
cpu_relax()
}
@@ -299,7 +298,7 @@ queue_item_wait :: proc(item: ^Queue_Item) {
@(private="file")
queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool {
start := time.tick_now()
for atomic_load_acquire(&item.futex) == 0 {
for atomic_load_explicit(&item.futex, .Acquire) == 0 {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
@@ -313,7 +312,7 @@ queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration)
}
@(private="file")
queue_item_signal :: proc(item: ^Queue_Item) {
atomic_store_release(&item.futex, 1)
atomic_store_explicit(&item.futex, 1, .Release)
futex_signal(&item.futex)
}

View File

@@ -1,6 +1,6 @@
//+build darwin
//+private
package sync2
package sync
import "core:c"
import "core:time"

View File

@@ -0,0 +1,46 @@
//+build freebsd
//+private
package sync
import "core:os"
import "core:time"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
_Mutex :: struct {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
_Cond :: struct {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}

View File

@@ -0,0 +1,125 @@
//+private
package sync
when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
_Recursive_Mutex :: struct {
owner: Futex,
recursion: i32,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return
}
futex_wait(&m.impl.owner, u32(prev_owner))
}
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
}
atomic_exchange_explicit(&m.impl.owner, 0, .Release)
futex_signal(&m.impl.owner)
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := Futex(current_thread_id())
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return true
}
return false
}
} else {
_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
if tid != m.impl.owner {
mutex_lock(&m.impl.mutex)
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
assert(tid == m.impl.owner)
m.impl.recursion -= 1
recursion := m.impl.recursion
if recursion == 0 {
m.impl.owner = 0
}
if recursion == 0 {
mutex_unlock(&m.impl.mutex)
}
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := current_thread_id()
if m.impl.owner == tid {
return mutex_try_lock(&m.impl.mutex)
}
if !mutex_try_lock(&m.impl.mutex) {
return false
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
return true
}
}
when ODIN_OS != .Windows {
_RW_Mutex :: struct {
mutex: Atomic_RW_Mutex,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_lock(&rw.impl.mutex)
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_unlock(&rw.impl.mutex)
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_lock(&rw.impl.mutex)
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_shared_lock(&rw.impl.mutex)
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
}
}

View File

@@ -0,0 +1,47 @@
//+build linux
//+private
package sync
import "core:sys/unix"
import "core:time"
_current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}
_Mutex :: struct {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
_Cond :: struct {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}

View File

@@ -0,0 +1,46 @@
//+build openbsd
//+private
package sync
import "core:os"
import "core:time"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
_Mutex :: struct {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
_Cond :: struct {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}

View File

@@ -1,6 +1,6 @@
//+build windows
//+private
package sync2
package sync
import "core:time"
import win32 "core:sys/windows"

View File

@@ -1,5 +1,5 @@
//+private
package sync2
package sync
import "core:time"

View File

@@ -1,123 +0,0 @@
package sync
import "core:intrinsics"
cpu_relax :: #force_inline proc "contextless" () {
intrinsics.cpu_relax()
}
Condition_Mutex_Ptr :: union{^Mutex, ^Blocking_Mutex}
Ticket_Mutex :: struct {
ticket: u64,
serving: u64,
}
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
atomic_store(&m.ticket, 0, .Relaxed)
atomic_store(&m.serving, 0, .Relaxed)
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add(&m.ticket, 1, .Relaxed)
for ticket != atomic_load(&m.serving, .Acquire) {
intrinsics.cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
atomic_add(&m.serving, 1, .Relaxed)
}
Benaphore :: struct {
counter: int,
sema: Semaphore,
}
benaphore_init :: proc(b: ^Benaphore) {
intrinsics.atomic_store(&b.counter, 0)
semaphore_init(&b.sema)
}
benaphore_destroy :: proc(b: ^Benaphore) {
semaphore_destroy(&b.sema)
}
benaphore_lock :: proc(b: ^Benaphore) {
if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
semaphore_wait_for(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
semaphore_post(&b.sema)
}
}
Recursive_Benaphore :: struct {
counter: int,
owner: int,
recursion: int,
sema: Semaphore,
}
recursive_benaphore_init :: proc(b: ^Recursive_Benaphore) {
intrinsics.atomic_store(&b.counter, 0)
semaphore_init(&b.sema)
}
recursive_benaphore_destroy :: proc(b: ^Recursive_Benaphore) {
semaphore_destroy(&b.sema)
}
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
if tid != b.owner {
semaphore_wait_for(&b.sema)
}
}
// inside the lock
b.owner = tid
b.recursion += 1
}
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
intrinsics.atomic_add_acq(&b.counter, 1)
} else {
v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
if v != 0 {
return false
}
// inside the lock
b.owner = tid
}
b.recursion += 1
return true
}
recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
assert(tid == b.owner)
b.recursion -= 1
recursion := b.recursion
if recursion == 0 {
b.owner = 0
}
if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
if recursion == 0 {
semaphore_post(&b.sema)
}
}
// outside the lock
}

View File

@@ -1,79 +0,0 @@
package sync2
import "core:intrinsics"
cpu_relax :: intrinsics.cpu_relax
atomic_fence :: intrinsics.atomic_fence
atomic_fence_acquire :: intrinsics.atomic_fence_acq
atomic_fence_release :: intrinsics.atomic_fence_rel
atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel
atomic_store :: intrinsics.atomic_store
atomic_store_release :: intrinsics.atomic_store_rel
atomic_store_relaxed :: intrinsics.atomic_store_relaxed
atomic_store_unordered :: intrinsics.atomic_store_unordered
atomic_load :: intrinsics.atomic_load
atomic_load_acquire :: intrinsics.atomic_load_acq
atomic_load_relaxed :: intrinsics.atomic_load_relaxed
atomic_load_unordered :: intrinsics.atomic_load_unordered
atomic_add :: intrinsics.atomic_add
atomic_add_acquire :: intrinsics.atomic_add_acq
atomic_add_release :: intrinsics.atomic_add_rel
atomic_add_acqrel :: intrinsics.atomic_add_acqrel
atomic_add_relaxed :: intrinsics.atomic_add_relaxed
atomic_sub :: intrinsics.atomic_sub
atomic_sub_acquire :: intrinsics.atomic_sub_acq
atomic_sub_release :: intrinsics.atomic_sub_rel
atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel
atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed
atomic_and :: intrinsics.atomic_and
atomic_and_acquire :: intrinsics.atomic_and_acq
atomic_and_release :: intrinsics.atomic_and_rel
atomic_and_acqrel :: intrinsics.atomic_and_acqrel
atomic_and_relaxed :: intrinsics.atomic_and_relaxed
atomic_nand :: intrinsics.atomic_nand
atomic_nand_acquire :: intrinsics.atomic_nand_acq
atomic_nand_release :: intrinsics.atomic_nand_rel
atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel
atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed
atomic_or :: intrinsics.atomic_or
atomic_or_acquire :: intrinsics.atomic_or_acq
atomic_or_release :: intrinsics.atomic_or_rel
atomic_or_acqrel :: intrinsics.atomic_or_acqrel
atomic_or_relaxed :: intrinsics.atomic_or_relaxed
atomic_xor :: intrinsics.atomic_xor
atomic_xor_acquire :: intrinsics.atomic_xor_acq
atomic_xor_release :: intrinsics.atomic_xor_rel
atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel
atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed
atomic_exchange :: intrinsics.atomic_xchg
atomic_exchange_acquire :: intrinsics.atomic_xchg_acq
atomic_exchange_release :: intrinsics.atomic_xchg_rel
atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel
atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed
// Returns value and optional ok boolean
atomic_compare_exchange_strong :: intrinsics.atomic_cxchg
atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq
atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel
atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel
atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed
atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed
atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq
atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed
atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed
// Returns value and optional ok boolean
atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak
atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq
atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel
atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel
atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed
atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed
atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq
atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed
atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed

View File

@@ -1,9 +0,0 @@
//+build freebsd
//+private
package sync2
import "core:os"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}

View File

@@ -1,184 +0,0 @@
//+private
package sync2
when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
_Recursive_Mutex :: struct {
owner: Futex,
recursion: i32,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return
}
futex_wait(&m.impl.owner, u32(prev_owner))
}
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
}
atomic_exchange_release(&m.impl.owner, 0)
futex_signal(&m.impl.owner)
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := Futex(current_thread_id())
prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return true
}
return false
}
} else {
_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
if tid != m.impl.owner {
mutex_lock(&m.impl.mutex)
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
assert(tid == m.impl.owner)
m.impl.recursion -= 1
recursion := m.impl.recursion
if recursion == 0 {
m.impl.owner = 0
}
if recursion == 0 {
mutex_unlock(&m.impl.mutex)
}
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := current_thread_id()
if m.impl.owner == tid {
return mutex_try_lock(&m.impl.mutex)
}
if !mutex_try_lock(&m.impl.mutex) {
return false
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
return true
}
}
when ODIN_OS != .Windows {
RW_Mutex_State :: distinct uint
RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2
RW_Mutex_State_Is_Writing :: RW_Mutex_State(1)
RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1
RW_Mutex_State_Reader :: RW_Mutex_State(1)<<RW_Mutex_State_Half_Width
RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1
RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width
_RW_Mutex :: struct {
// NOTE(bill): pthread_rwlock_t cannot be used since pthread_rwlock_destroy is required on some platforms
// TODO(bill): Can we determine which platforms exactly?
state: RW_Mutex_State,
mutex: Mutex,
sema: Sema,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer)
mutex_lock(&rw.impl.mutex)
state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer)
if state & RW_Mutex_State_Reader_Mask != 0 {
sema_wait(&rw.impl.sema)
}
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
_ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing)
mutex_unlock(&rw.impl.mutex)
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
if mutex_try_lock(&rw.impl.mutex) {
state := atomic_load(&rw.impl.state)
if state & RW_Mutex_State_Reader_Mask == 0 {
_ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing)
return true
}
mutex_unlock(&rw.impl.mutex)
}
return false
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
state := atomic_load(&rw.impl.state)
for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
ok: bool
state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader)
if ok {
return
}
}
mutex_lock(&rw.impl.mutex)
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
mutex_unlock(&rw.impl.mutex)
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader)
if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
(state & RW_Mutex_State_Is_Writing != 0) {
sema_post(&rw.impl.sema)
}
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
state := atomic_load(&rw.impl.state)
if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
_, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader)
if ok {
return true
}
}
if mutex_try_lock(&rw.impl.mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
mutex_unlock(&rw.impl.mutex)
return true
}
return false
}
}

View File

@@ -1,9 +0,0 @@
//+build linux
//+private
package sync2
import "core:sys/unix"
_current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}

View File

@@ -1,9 +0,0 @@
//+build openbsd
//+private
package sync2
import "core:os"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}

View File

@@ -1,58 +0,0 @@
//+build linux, freebsd, openbsd
//+private
package sync2
import "core:time"
import "core:sys/unix"
_Mutex_State :: enum i32 {
Unlocked = 0,
Locked = 1,
Waiting = 2,
}
_Mutex :: struct {
pthread_mutex: unix.pthread_mutex_t,
}
_mutex_lock :: proc(m: ^Mutex) {
err := unix.pthread_mutex_lock(&m.impl.pthread_mutex)
assert(err == 0)
}
_mutex_unlock :: proc(m: ^Mutex) {
err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex)
assert(err == 0)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex)
return err == 0
}
_Cond :: struct {
pthread_cond: unix.pthread_cond_t,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex)
assert(err == 0)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
tv_sec := i64(duration/1e9)
tv_nsec := i64(duration%1e9)
err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, &{tv_sec, tv_nsec})
return err == 0
}
_cond_signal :: proc(c: ^Cond) {
err := unix.pthread_cond_signal(&c.impl.pthread_cond)
assert(err == 0)
}
_cond_broadcast :: proc(c: ^Cond) {
err := unix.pthread_cond_broadcast(&c.impl.pthread_cond)
assert(err == 0)
}

View File

@@ -1,54 +0,0 @@
package sync
import "core:sys/darwin"
import "core:c"
foreign import pthread "System.framework"
current_thread_id :: proc "contextless" () -> int {
tid: u64
// NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
// For older versions there is `syscall(SYS_thread_selfid)`, but not really
// the same thing apparently.
foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
pthread_threadid_np(nil, &tid)
return int(tid)
}
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: darwin.semaphore_t,
}
// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
// See core/sys/unix/pthread_linux.odin/pthread_t.
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
ct := darwin.mach_task_self()
res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count))
assert(res == 0)
}
semaphore_destroy :: proc(s: ^Semaphore) {
ct := darwin.mach_task_self()
res := darwin.semaphore_destroy(ct, s.handle)
assert(res == 0)
s.handle = {}
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..<count {
res := darwin.semaphore_signal(s.handle)
assert(res == 0)
}
}
semaphore_wait_for :: proc(s: ^Semaphore) {
res := darwin.semaphore_wait(s.handle)
assert(res == 0)
}

View File

@@ -1,40 +0,0 @@
package sync
import "core:sys/unix"
import "core:intrinsics"
current_thread_id :: proc "contextless" () -> int {
SYS_GETTID :: 186
return int(intrinsics.syscall(SYS_GETTID))
}
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: unix.sem_t,
}
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
}
semaphore_destroy :: proc(s: ^Semaphore) {
assert(unix.sem_destroy(&s.handle) == 0)
s.handle = {}
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..<count {
assert(unix.sem_post(&s.handle) == 0)
}
}
semaphore_wait_for :: proc(s: ^Semaphore) {
assert(unix.sem_wait(&s.handle) == 0)
}

View File

@@ -1,36 +0,0 @@
package sync
import "core:sys/unix"
current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: unix.sem_t,
}
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
}
semaphore_destroy :: proc(s: ^Semaphore) {
assert(unix.sem_destroy(&s.handle) == 0)
s.handle = {}
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..<count {
assert(unix.sem_post(&s.handle) == 0)
}
}
semaphore_wait_for :: proc(s: ^Semaphore) {
assert(unix.sem_wait(&s.handle) == 0)
}

View File

@@ -1,36 +0,0 @@
package sync
import "core:sys/unix"
import "core:os"
current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: unix.sem_t,
}
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
}
semaphore_destroy :: proc(s: ^Semaphore) {
assert(unix.sem_destroy(&s.handle) == 0)
s.handle = {}
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..<count {
assert(unix.sem_post(&s.handle) == 0)
}
}
semaphore_wait_for :: proc(s: ^Semaphore) {
assert(unix.sem_wait(&s.handle) == 0)
}

View File

@@ -1,248 +0,0 @@
// +build linux, darwin, freebsd, openbsd
package sync
import "core:sys/unix"
import "core:time"
// A recursive lock that can only be held by one thread at once
Mutex :: struct {
handle: unix.pthread_mutex_t,
}
mutex_init :: proc(m: ^Mutex) {
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
attrs: unix.pthread_mutexattr_t
assert(unix.pthread_mutexattr_init(&attrs) == 0)
defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error
unix.pthread_mutexattr_settype(&attrs, unix.PTHREAD_MUTEX_RECURSIVE)
assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
}
mutex_destroy :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_destroy(&m.handle) == 0)
m.handle = {}
}
mutex_lock :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_lock(&m.handle) == 0)
}
// Returns false if someone else holds the lock.
mutex_try_lock :: proc(m: ^Mutex) -> bool {
return unix.pthread_mutex_trylock(&m.handle) == 0
}
mutex_unlock :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_unlock(&m.handle) == 0)
}
Blocking_Mutex :: struct {
handle: unix.pthread_mutex_t,
}
blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
attrs: unix.pthread_mutexattr_t
assert(unix.pthread_mutexattr_init(&attrs) == 0)
defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error
assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
}
blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
assert(unix.pthread_mutex_destroy(&m.handle) == 0)
m.handle = {}
}
blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
assert(unix.pthread_mutex_lock(&m.handle) == 0)
}
// Returns false if someone else holds the lock.
blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
return unix.pthread_mutex_trylock(&m.handle) == 0
}
blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
assert(unix.pthread_mutex_unlock(&m.handle) == 0)
}
// Blocks until signalled, and then lets past exactly
// one thread.
Condition :: struct {
handle: unix.pthread_cond_t,
mutex: Condition_Mutex_Ptr,
// NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
// This means that you may signal the condition before anyone is waiting to cause the
// next thread that tries to wait to just pass by uninterrupted, without sleeping.
// Without this, signalling a condition will only wake up a thread which is already waiting,
// but not one that is about to wait, which can cause your program to become out of sync in
// ways that are hard to debug or fix.
flag: bool, // atomically mutated
}
condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
attrs: unix.pthread_condattr_t
if unix.pthread_condattr_init(&attrs) != 0 {
return false
}
defer unix.pthread_condattr_destroy(&attrs) // ignores destruction error
c.flag = false
c.mutex = mutex
return unix.pthread_cond_init(&c.handle, &attrs) == 0
}
condition_destroy :: proc(c: ^Condition) {
assert(unix.pthread_cond_destroy(&c.handle) == 0)
c.handle = {}
}
// Awaken exactly one thread who is waiting on the condition
condition_signal :: proc(c: ^Condition) -> bool {
switch m in c.mutex {
case ^Mutex:
mutex_lock(m)
defer mutex_unlock(m)
atomic_swap(&c.flag, true, .Sequentially_Consistent)
return unix.pthread_cond_signal(&c.handle) == 0
case ^Blocking_Mutex:
blocking_mutex_lock(m)
defer blocking_mutex_unlock(m)
atomic_swap(&c.flag, true, .Sequentially_Consistent)
return unix.pthread_cond_signal(&c.handle) == 0
}
return false
}
// Awaken all threads who are waiting on the condition
condition_broadcast :: proc(c: ^Condition) -> bool {
return unix.pthread_cond_broadcast(&c.handle) == 0
}
// Wait for the condition to be signalled.
// Does not block if the condition has been signalled and no one
// has waited on it yet.
condition_wait_for :: proc(c: ^Condition) -> bool {
switch m in c.mutex {
case ^Mutex:
mutex_lock(m)
defer mutex_unlock(m)
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
// back to sleep.
// Though this overall behavior is the most sane, there may be a better way to do this that means that
// the first thread to wait, gets the flag first.
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
for {
if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
return false
}
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
}
return false
case ^Blocking_Mutex:
blocking_mutex_lock(m)
defer blocking_mutex_unlock(m)
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
// back to sleep.
// Though this overall behavior is the most sane, there may be a better way to do this that means that
// the first thread to wait, gets the flag first.
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
for {
if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
return false
}
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
}
return false
}
return false
}
// Wait for the condition to be signalled.
// Does not block if the condition has been signalled and no one
// has waited on it yet.
condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
switch m in c.mutex {
case ^Mutex:
mutex_lock(m)
defer mutex_unlock(m)
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
// back to sleep.
// Though this overall behavior is the most sane, there may be a better way to do this that means that
// the first thread to wait, gets the flag first.
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
ns := time.duration_nanoseconds(duration)
timeout: time.TimeSpec
timeout.tv_sec = ns / 1e9
timeout.tv_nsec = ns % 1e9
for {
if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
return false
}
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
}
return false
case ^Blocking_Mutex:
blocking_mutex_lock(m)
defer blocking_mutex_unlock(m)
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
// back to sleep.
// Though this overall behavior is the most sane, there may be a better way to do this that means that
// the first thread to wait, gets the flag first.
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
ns := time.duration_nanoseconds(duration)
timeout: time.TimeSpec
timeout.tv_sec = ns / 1e9
timeout.tv_nsec = ns % 1e9
for {
if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
return false
}
if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
return true
}
}
return false
}
return false
}
thread_yield :: proc() {
unix.sched_yield()
}

View File

@@ -1,4 +1,4 @@
package sync2
package sync
/*
Example:

View File

@@ -1,180 +0,0 @@
// +build windows
package sync
import win32 "core:sys/windows"
import "core:time"
current_thread_id :: proc "contextless" () -> int {
return int(win32.GetCurrentThreadId())
}
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
Semaphore :: struct {
_handle: win32.HANDLE,
}
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil)
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.CloseHandle(s._handle)
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
win32.ReleaseSemaphore(s._handle, i32(count), nil)
}
semaphore_wait_for :: proc(s: ^Semaphore) {
// NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
result := win32.WaitForSingleObject(s._handle, win32.INFINITE)
assert(result != win32.WAIT_FAILED)
}
Mutex :: struct {
_critical_section: win32.CRITICAL_SECTION,
}
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count))
}
mutex_destroy :: proc(m: ^Mutex) {
win32.DeleteCriticalSection(&m._critical_section)
}
mutex_lock :: proc(m: ^Mutex) {
win32.EnterCriticalSection(&m._critical_section)
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
return bool(win32.TryEnterCriticalSection(&m._critical_section))
}
mutex_unlock :: proc(m: ^Mutex) {
win32.LeaveCriticalSection(&m._critical_section)
}
Blocking_Mutex :: struct {
_handle: win32.SRWLOCK,
}
blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
win32.InitializeSRWLock(&m._handle)
}
blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
//
}
blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
win32.AcquireSRWLockExclusive(&m._handle)
}
blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
return bool(win32.TryAcquireSRWLockExclusive(&m._handle))
}
blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
win32.ReleaseSRWLockExclusive(&m._handle)
}
// Blocks until signalled.
// When signalled, awakens exactly one waiting thread.
Condition :: struct {
_handle: win32.CONDITION_VARIABLE,
mutex: Condition_Mutex_Ptr,
}
condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
assert(mutex != nil)
win32.InitializeConditionVariable(&c._handle)
c.mutex = mutex
return true
}
condition_destroy :: proc(c: ^Condition) {
//
}
condition_signal :: proc(c: ^Condition) -> bool {
if c._handle.ptr == nil {
return false
}
win32.WakeConditionVariable(&c._handle)
return true
}
condition_broadcast :: proc(c: ^Condition) -> bool {
if c._handle.ptr == nil {
return false
}
win32.WakeAllConditionVariable(&c._handle)
return true
}
condition_wait_for :: proc(c: ^Condition) -> bool {
switch m in &c.mutex {
case ^Mutex:
return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE)
case ^Blocking_Mutex:
return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0)
}
return false
}
condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000)
switch m in &c.mutex {
case ^Mutex:
return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms)
case ^Blocking_Mutex:
return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0)
}
return false
}
RW_Lock :: struct {
_handle: win32.SRWLOCK,
}
rw_lock_init :: proc(l: ^RW_Lock) {
l._handle = win32.SRWLOCK_INIT
}
rw_lock_destroy :: proc(l: ^RW_Lock) {
//
}
rw_lock_read :: proc(l: ^RW_Lock) {
win32.AcquireSRWLockShared(&l._handle)
}
rw_lock_try_read :: proc(l: ^RW_Lock) -> bool {
return bool(win32.TryAcquireSRWLockShared(&l._handle))
}
rw_lock_write :: proc(l: ^RW_Lock) {
win32.AcquireSRWLockExclusive(&l._handle)
}
rw_lock_try_write :: proc(l: ^RW_Lock) -> bool {
return bool(win32.TryAcquireSRWLockExclusive(&l._handle))
}
rw_lock_read_unlock :: proc(l: ^RW_Lock) {
win32.ReleaseSRWLockShared(&l._handle)
}
rw_lock_write_unlock :: proc(l: ^RW_Lock) {
win32.ReleaseSRWLockExclusive(&l._handle)
}
thread_yield :: proc() {
win32.SwitchToThread()
}

View File

@@ -1,58 +0,0 @@
package sync
import "core:intrinsics"
Wait_Group :: struct {
counter: int,
mutex: Blocking_Mutex,
cond: Condition,
}
wait_group_init :: proc(wg: ^Wait_Group) {
wg.counter = 0
blocking_mutex_init(&wg.mutex)
condition_init(&wg.cond, &wg.mutex)
}
wait_group_destroy :: proc(wg: ^Wait_Group) {
condition_destroy(&wg.cond)
blocking_mutex_destroy(&wg.mutex)
}
wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
if delta == 0 {
return
}
blocking_mutex_lock(&wg.mutex)
defer blocking_mutex_unlock(&wg.mutex)
intrinsics.atomic_add(&wg.counter, delta)
if wg.counter < 0 {
panic("sync.Wait_Group negative counter")
}
if wg.counter == 0 {
condition_broadcast(&wg.cond)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
wait_group_done :: proc(wg: ^Wait_Group) {
wait_group_add(wg, -1)
}
wait_group_wait :: proc(wg: ^Wait_Group) {
blocking_mutex_lock(&wg.mutex)
defer blocking_mutex_unlock(&wg.mutex)
if wg.counter != 0 {
condition_wait_for(&wg.cond)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}

View File

@@ -111,6 +111,8 @@ foreign user32 {
@(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---
@(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 ---
@(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 ---
@(link_name="UnregisterClassA") unregister_class_a :: proc(class_name: cstring, instance: Hinstance) -> Bool ---
@(link_name="UnregisterClassW") unregister_class_w :: proc(class_name: Wstring, instance: Hinstance) -> Bool ---
@(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---
@(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---

View File

@@ -0,0 +1,66 @@
// +build windows
package sys_windows
foreign import gdi32 "system:Gdi32.lib"
@(default_calling_convention="stdcall")
foreign gdi32 {
GetStockObject :: proc(i: c_int) -> HGDIOBJ ---
SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
CreateDIBitmap :: proc(
hdc: HDC,
pbmih: ^BITMAPINFOHEADER,
flInit: DWORD,
pjBits: VOID,
pbmi: ^BITMAPINFO,
iUsage: UINT,
) -> HBITMAP ---
CreateDIBSection :: proc(
hdc: HDC,
pbmi: ^BITMAPINFO,
usage: UINT,
ppvBits: VOID,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP ---
StretchDIBits :: proc(
hdc: HDC,
xDest: c_int,
yDest: c_int,
DestWidth: c_int,
DestHeight: c_int,
xSrc: c_int,
ySrc: c_int,
SrcWidth: c_int,
SrcHeight: c_int,
lpBits: VOID,
lpbmi: ^BITMAPINFO,
iUsage: UINT,
rop: DWORD,
) -> c_int ---
StretchBlt :: proc(
hdcDest: HDC,
xDest: c_int,
yDest: c_int,
wDest: c_int,
hDest: c_int,
hdcSrc: HDC,
xSrc: c_int,
ySrc: c_int,
wSrc: c_int,
hSrc: c_int,
rop: DWORD,
) -> BOOL ---
SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
SwapBuffers :: proc(HDC) -> BOOL ---
PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
}

View File

@@ -341,6 +341,7 @@ MEM_TOP_DOWN :: 0x100000
MEM_LARGE_PAGES :: 0x20000000
MEM_4MB_PAGES :: 0x80000000
@(default_calling_convention="stdcall")
foreign kernel32 {
VirtualAlloc :: proc(
lpAddress: LPVOID,
@@ -483,6 +484,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes
HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification
@(default_calling_convention="stdcall")
foreign kernel32 {
CreateMemoryResourceNotification :: proc(
NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE,
@@ -498,6 +500,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002)
FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004)
FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008)
@(default_calling_convention="stdcall")
foreign kernel32 {
GetSystemFileCacheSize :: proc(
lpMinimumFileCacheSize: PSIZE_T,
@@ -527,6 +530,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
@(default_calling_convention="stdcall")
foreign kernel32 {
PrefetchVirtualMemory :: proc(
hProcess: HANDLE,
@@ -584,6 +588,7 @@ foreign kernel32 {
MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
@(default_calling_convention="stdcall")
foreign kernel32 {
GetMemoryErrorHandlingCapabilities :: proc(
Capabilities: PULONG,
@@ -592,6 +597,7 @@ foreign kernel32 {
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
@(default_calling_convention="stdcall")
foreign kernel32 {
RegisterBadMemoryNotification :: proc(
Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -612,6 +618,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow
VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal
@(default_calling_convention="stdcall")
foreign kernel32 {
OfferVirtualMemory :: proc(
VirtualAddress: PVOID,
@@ -676,6 +683,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
Reserved : 32-6,
}*/
@(default_calling_convention="stdcall")
foreign kernel32 {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
@@ -700,7 +708,7 @@ foreign kernel32 {
NUMA_NO_PREFERRED_NODE :: 0xffffffff
MapViewOfFile2 :: #force_inline proc(
MapViewOfFile2 :: #force_inline proc "stdcall" (
FileMappingHandle: HANDLE,
ProcessHandle: HANDLE,
Offset: ULONG64,
@@ -721,6 +729,7 @@ MapViewOfFile2 :: #force_inline proc(
)
}
@(default_calling_convention="stdcall")
foreign kernel32 {
UnmapViewOfFile2 :: proc(
ProcessHandle: HANDLE,

View File

@@ -0,0 +1,252 @@
// +build windows
package sys_windows
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
// Virtual Keys, Standard Set
VK_LBUTTON :: 0x01
VK_RBUTTON :: 0x02
VK_CANCEL :: 0x03
VK_MBUTTON :: 0x04 // NOT contiguous with L & RBUTTON
VK_XBUTTON1 :: 0x05 // NOT contiguous with L & RBUTTON
VK_XBUTTON2 :: 0x06 // NOT contiguous with L & RBUTTON
// 0x07 : reserved
VK_BACK :: 0x08
VK_TAB :: 0x09
// 0x0A - 0x0B : reserved
VK_CLEAR :: 0x0C
VK_RETURN :: 0x0D
// 0x0E - 0x0F : unassigned
VK_SHIFT :: 0x10
VK_CONTROL :: 0x11
VK_MENU :: 0x12
VK_PAUSE :: 0x13
VK_CAPITAL :: 0x14
VK_KANA :: 0x15
VK_HANGEUL :: 0x15 // old name - should be here for compatibility
VK_HANGUL :: 0x15
VK_IME_ON :: 0x16
VK_JUNJA :: 0x17
VK_FINAL :: 0x18
VK_HANJA :: 0x19
VK_KANJI :: 0x19
VK_IME_OFF :: 0x1A
VK_ESCAPE :: 0x1B
VK_CONVERT :: 0x1C
VK_NONCONVERT :: 0x1D
VK_ACCEPT :: 0x1E
VK_MODECHANGE :: 0x1F
VK_SPACE :: 0x20
VK_PRIOR :: 0x21
VK_NEXT :: 0x22
VK_END :: 0x23
VK_HOME :: 0x24
VK_LEFT :: 0x25
VK_UP :: 0x26
VK_RIGHT :: 0x27
VK_DOWN :: 0x28
VK_SELECT :: 0x29
VK_PRINT :: 0x2A
VK_EXECUTE :: 0x2B
VK_SNAPSHOT :: 0x2C
VK_INSERT :: 0x2D
VK_DELETE :: 0x2E
VK_HELP :: 0x2F
VK_0 :: '0'
VK_1 :: '1'
VK_2 :: '2'
VK_3 :: '3'
VK_4 :: '4'
VK_5 :: '5'
VK_6 :: '6'
VK_7 :: '7'
VK_8 :: '8'
VK_9 :: '9'
// 0x3A - 0x40 : unassigned
VK_A :: 'A'
VK_B :: 'B'
VK_C :: 'C'
VK_D :: 'D'
VK_E :: 'E'
VK_F :: 'F'
VK_G :: 'G'
VK_H :: 'H'
VK_I :: 'I'
VK_J :: 'J'
VK_K :: 'K'
VK_L :: 'L'
VK_M :: 'M'
VK_N :: 'N'
VK_O :: 'O'
VK_P :: 'P'
VK_Q :: 'Q'
VK_R :: 'R'
VK_S :: 'S'
VK_T :: 'T'
VK_U :: 'U'
VK_V :: 'V'
VK_W :: 'W'
VK_X :: 'X'
VK_Y :: 'Y'
VK_Z :: 'Z'
VK_LWIN :: 0x5B
VK_RWIN :: 0x5C
VK_APPS :: 0x5D
// 0x5E : reserved
VK_SLEEP :: 0x5F
VK_NUMPAD0 :: 0x60
VK_NUMPAD1 :: 0x61
VK_NUMPAD2 :: 0x62
VK_NUMPAD3 :: 0x63
VK_NUMPAD4 :: 0x64
VK_NUMPAD5 :: 0x65
VK_NUMPAD6 :: 0x66
VK_NUMPAD7 :: 0x67
VK_NUMPAD8 :: 0x68
VK_NUMPAD9 :: 0x69
VK_MULTIPLY :: 0x6A
VK_ADD :: 0x6B
VK_SEPARATOR :: 0x6C
VK_SUBTRACT :: 0x6D
VK_DECIMAL :: 0x6E
VK_DIVIDE :: 0x6F
VK_F1 :: 0x70
VK_F2 :: 0x71
VK_F3 :: 0x72
VK_F4 :: 0x73
VK_F5 :: 0x74
VK_F6 :: 0x75
VK_F7 :: 0x76
VK_F8 :: 0x77
VK_F9 :: 0x78
VK_F10 :: 0x79
VK_F11 :: 0x7A
VK_F12 :: 0x7B
VK_F13 :: 0x7C
VK_F14 :: 0x7D
VK_F15 :: 0x7E
VK_F16 :: 0x7F
VK_F17 :: 0x80
VK_F18 :: 0x81
VK_F19 :: 0x82
VK_F20 :: 0x83
VK_F21 :: 0x84
VK_F22 :: 0x85
VK_F23 :: 0x86
VK_F24 :: 0x87
// 0x88 - 0x8F : reserved
VK_NUMLOCK :: 0x90
VK_SCROLL :: 0x91
// NEC PC-9800 kbd definitions
VK_OEM_NEC_EQUAL :: 0x92 // '=' key on numpad
// Fujitsu/OASYS kbd definitions
VK_OEM_FJ_JISHO :: 0x92 // 'Dictionary' key
VK_OEM_FJ_MASSHOU :: 0x93 // 'Unregister word' key
VK_OEM_FJ_TOUROKU :: 0x94 // 'Register word' key
VK_OEM_FJ_LOYA :: 0x95 // 'Left OYAYUBI' key
VK_OEM_FJ_ROYA :: 0x96 // 'Right OYAYUBI' key
// 0x97 - 0x9F : unassigned
// VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
// Used only as parameters to GetAsyncKeyState() and GetKeyState().
// No other API or message will distinguish left and right keys in this way.
VK_LSHIFT :: 0xA0
VK_RSHIFT :: 0xA1
VK_LCONTROL :: 0xA2
VK_RCONTROL :: 0xA3
VK_LMENU :: 0xA4
VK_RMENU :: 0xA5
VK_BROWSER_BACK :: 0xA6
VK_BROWSER_FORWARD :: 0xA7
VK_BROWSER_REFRESH :: 0xA8
VK_BROWSER_STOP :: 0xA9
VK_BROWSER_SEARCH :: 0xAA
VK_BROWSER_FAVORITES :: 0xAB
VK_BROWSER_HOME :: 0xAC
VK_VOLUME_MUTE :: 0xAD
VK_VOLUME_DOWN :: 0xAE
VK_VOLUME_UP :: 0xAF
VK_MEDIA_NEXT_TRACK :: 0xB0
VK_MEDIA_PREV_TRACK :: 0xB1
VK_MEDIA_STOP :: 0xB2
VK_MEDIA_PLAY_PAUSE :: 0xB3
VK_LAUNCH_MAIL :: 0xB4
VK_LAUNCH_MEDIA_SELECT :: 0xB5
VK_LAUNCH_APP1 :: 0xB6
VK_LAUNCH_APP2 :: 0xB7
// 0xB8 - 0xB9 : reserved
VK_OEM_1 :: 0xBA // ';:' for US
VK_OEM_PLUS :: 0xBB // '+' any country
VK_OEM_COMMA :: 0xBC // ',' any country
VK_OEM_MINUS :: 0xBD // '-' any country
VK_OEM_PERIOD :: 0xBE // '.' any country
VK_OEM_2 :: 0xBF // '/?' for US
VK_OEM_3 :: 0xC0 // '`~' for US
// 0xC1 - 0xDA : reserved
VK_OEM_4 :: 0xDB // '[{' for US
VK_OEM_5 :: 0xDC // '\|' for US
VK_OEM_6 :: 0xDD // ']}' for US
VK_OEM_7 :: 0xDE // ''"' for US
VK_OEM_8 :: 0xDF
// 0xE0 : reserved
// Various extended or enhanced keyboards
VK_OEM_AX :: 0xE1 // 'AX' key on Japanese AX kbd
VK_OEM_102 :: 0xE2 // "<>" or "\|" on RT 102-key kbd.
VK_ICO_HELP :: 0xE3 // Help key on ICO
VK_ICO_00 :: 0xE4 // 00 key on ICO
VK_PROCESSKEY :: 0xE5
VK_ICO_CLEAR :: 0xE6
VK_PACKET :: 0xE7
// 0xE8 : unassigned
// Nokia/Ericsson definitions
VK_OEM_RESET :: 0xE9
VK_OEM_JUMP :: 0xEA
VK_OEM_PA1 :: 0xEB
VK_OEM_PA2 :: 0xEC
VK_OEM_PA3 :: 0xED
VK_OEM_WSCTRL :: 0xEE
VK_OEM_CUSEL :: 0xEF
VK_OEM_ATTN :: 0xF0
VK_OEM_FINISH :: 0xF1
VK_OEM_COPY :: 0xF2
VK_OEM_AUTO :: 0xF3
VK_OEM_ENLW :: 0xF4
VK_OEM_BACKTAB :: 0xF5
VK_ATTN :: 0xF6
VK_CRSEL :: 0xF7
VK_EXSEL :: 0xF8
VK_EREOF :: 0xF9
VK_PLAY :: 0xFA
VK_ZOOM :: 0xFB
VK_NONAME :: 0xFC
VK_PA1 :: 0xFD
VK_OEM_CLEAR :: 0xFE
// 0xFF : reserved

View File

@@ -21,7 +21,15 @@ HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
HWND :: distinct HANDLE
HDC :: distinct HANDLE
HMONITOR :: distinct HANDLE
HICON :: distinct HANDLE
HCURSOR :: distinct HANDLE
HMENU :: distinct HANDLE
HBRUSH :: distinct HANDLE
HGDIOBJ :: distinct HANDLE
HBITMAP :: distinct HANDLE
HGLOBAL :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -42,9 +50,15 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
UINT_PTR :: uintptr
ULONG :: c_ulong
UCHAR :: BYTE
NTSTATUS :: c.long
LPARAM :: LONG_PTR
WPARAM :: UINT_PTR
LRESULT :: LONG_PTR
LPRECT :: ^RECT
LPPOINT :: ^POINT
UINT8 :: u8
UINT16 :: u16
@@ -71,6 +85,7 @@ PBOOL :: ^BOOL
LPBOOL :: ^BOOL
LPCSTR :: cstring
LPCWSTR :: wstring
LPCTSTR :: wstring
LPDWORD :: ^DWORD
PCSTR :: cstring
PCWSTR :: wstring
@@ -81,7 +96,8 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSTARTUPINFO :: ^STARTUPINFO
PVOID :: rawptr
VOID :: rawptr
PVOID :: rawptr
LPVOID :: rawptr
PINT :: ^INT
LPINT :: ^INT
@@ -178,6 +194,84 @@ GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
WNDCLASSA :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCSTR,
lpszClassName: LPCSTR,
}
WNDCLASSW :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCWSTR,
lpszClassName: LPCWSTR,
}
WNDCLASSEXA :: struct {
cbSize: UINT,
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCSTR,
lpszClassName: LPCSTR,
hIconSm: HICON,
}
WNDCLASSEXW :: struct {
cbSize: UINT,
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCWSTR,
lpszClassName: LPCWSTR,
hIconSm: HICON,
}
MSG :: struct {
hwnd: HWND,
message: UINT,
wParam: WPARAM,
lParam: LPARAM,
time: DWORD,
pt: POINT,
}
PAINTSTRUCT :: struct {
hdc: HDC,
fErase: BOOL,
rcPaint: RECT,
fRestore: BOOL,
fIncUpdate: BOOL,
rgbReserved: [32]BYTE,
}
WIN32_FIND_DATAW :: struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
@@ -191,6 +285,602 @@ WIN32_FIND_DATAW :: struct {
cAlternateFileName: [14]wchar_t,
}
CREATESTRUCTA :: struct {
lpCreateParams: LPVOID,
hInstance: HINSTANCE,
hMenu: HMENU,
hwndParent: HWND,
cy: c_int,
cx: c_int,
y: c_int,
x: c_int,
style: LONG,
lpszName: LPCSTR,
lpszClass: LPCSTR,
dwExStyle: DWORD,
}
CREATESTRUCTW:: struct {
lpCreateParams: LPVOID,
hInstance: HINSTANCE,
hMenu: HMENU,
hwndParent: HWND,
cy: c_int,
cx: c_int,
y: c_int,
x: c_int,
style: LONG,
lpszName: LPCWSTR,
lpszClass: LPCWSTR,
dwExStyle: DWORD,
}
// MessageBox() Flags
MB_OK :: 0x00000000
MB_OKCANCEL :: 0x00000001
MB_ABORTRETRYIGNORE :: 0x00000002
MB_YESNOCANCEL :: 0x00000003
MB_YESNO :: 0x00000004
MB_RETRYCANCEL :: 0x00000005
MB_CANCELTRYCONTINUE :: 0x00000006
MB_ICONHAND :: 0x00000010
MB_ICONQUESTION :: 0x00000020
MB_ICONEXCLAMATION :: 0x00000030
MB_ICONASTERISK :: 0x00000040
MB_USERICON :: 0x00000080
MB_ICONWARNING :: MB_ICONEXCLAMATION
MB_ICONERROR :: MB_ICONHAND
MB_ICONINFORMATION :: MB_ICONASTERISK
MB_ICONSTOP :: MB_ICONHAND
MB_DEFBUTTON1 :: 0x00000000
MB_DEFBUTTON2 :: 0x00000100
MB_DEFBUTTON3 :: 0x00000200
MB_DEFBUTTON4 :: 0x00000300
MB_APPLMODAL :: 0x00000000
MB_SYSTEMMODAL :: 0x00001000
MB_TASKMODAL :: 0x00002000
MB_HELP :: 0x00004000 // Help Button
MB_NOFOCUS :: 0x00008000
MB_SETFOREGROUND :: 0x00010000
MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
MB_TOPMOST :: 0x00040000
MB_RIGHT :: 0x00080000
MB_RTLREADING :: 0x00100000
MB_SERVICE_NOTIFICATION :: 0x00200000
MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000
MB_TYPEMASK :: 0x0000000F
MB_ICONMASK :: 0x000000F0
MB_DEFMASK :: 0x00000F00
MB_MODEMASK :: 0x00003000
MB_MISCMASK :: 0x0000C000
// Dialog Box Command IDs
IDOK :: 1
IDCANCEL :: 2
IDABORT :: 3
IDRETRY :: 4
IDIGNORE :: 5
IDYES :: 6
IDNO :: 7
IDCLOSE :: 8
IDHELP :: 9
IDTRYAGAIN :: 10
IDCONTINUE :: 11
IDTIMEOUT :: 32000
CS_VREDRAW : UINT : 0x0001
CS_HREDRAW : UINT : 0x0002
CS_DBLCLKS : UINT : 0x0008
CS_OWNDC : UINT : 0x0020
CS_CLASSDC : UINT : 0x0040
CS_PARENTDC : UINT : 0x0080
CS_NOCLOSE : UINT : 0x0200
CS_SAVEBITS : UINT : 0x0800
CS_BYTEALIGNCLIENT : UINT : 0x1000
CS_BYTEALIGNWINDOW : UINT : 0x2000
CS_GLOBALCLASS : UINT : 0x4000
CS_DROPSHADOW : UINT : 0x0002_0000
GWL_EXSTYLE : c_int : -20
GWLP_HINSTANCE : c_int : -6
GWLP_ID : c_int : -12
GWL_STYLE : c_int : -16
GWLP_USERDATA : c_int : -21
GWLP_WNDPROC : c_int : -4
WS_BORDER : UINT : 0x0080_0000
WS_CAPTION : UINT : 0x00C0_0000
WS_CHILD : UINT : 0x4000_0000
WS_CHILDWINDOW : UINT : WS_CHILD
WS_CLIPCHILDREN : UINT : 0x0200_0000
WS_CLIPSIBLINGS : UINT : 0x0400_0000
WS_DISABLED : UINT : 0x0800_0000
WS_DLGFRAME : UINT : 0x0040_0000
WS_GROUP : UINT : 0x0002_0000
WS_HSCROLL : UINT : 0x0010_0000
WS_ICONIC : UINT : 0x2000_0000
WS_MAXIMIZE : UINT : 0x0100_0000
WS_MAXIMIZEBOX : UINT : 0x0001_0000
WS_MINIMIZE : UINT : 0x2000_0000
WS_MINIMIZEBOX : UINT : 0x0002_0000
WS_OVERLAPPED : UINT : 0x0000_0000
WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
WS_POPUP : UINT : 0x8000_0000
WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU
WS_SIZEBOX : UINT : 0x0004_0000
WS_SYSMENU : UINT : 0x0008_0000
WS_TABSTOP : UINT : 0x0001_0000
WS_THICKFRAME : UINT : 0x0004_0000
WS_TILED : UINT : 0x0000_0000
WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE
WS_VISIBLE : UINT : 0x1000_0000
WS_VSCROLL : UINT : 0x0020_0000
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
QS_ALLPOSTMESSAGE : UINT : 0x0100
QS_HOTKEY : UINT : 0x0080
QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT
QS_KEY : UINT : 0x0001
QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON
QS_MOUSEBUTTON : UINT : 0x0004
QS_MOUSEMOVE : UINT : 0x0002
QS_PAINT : UINT : 0x0020
QS_POSTMESSAGE : UINT : 0x0008
QS_RAWINPUT : UINT : 0x0400
QS_SENDMESSAGE : UINT : 0x0040
QS_TIMER : UINT : 0x0010
PM_NOREMOVE : UINT : 0x0000
PM_REMOVE : UINT : 0x0001
PM_NOYIELD : UINT : 0x0002
PM_QS_INPUT : UINT : QS_INPUT << 16
PM_QS_PAINT : UINT : QS_PAINT << 16
PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16
PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16
SW_HIDE : c_int : 0
SW_SHOWNORMAL : c_int : SW_NORMAL
SW_NORMAL : c_int : 1
SW_SHOWMINIMIZED : c_int : 2
SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE
SW_MAXIMIZE : c_int : 3
SW_SHOWNOACTIVATE : c_int : 4
SW_SHOW : c_int : 5
SW_MINIMIZE : c_int : 6
SW_SHOWMINNOACTIVE : c_int : 7
SW_SHOWNA : c_int : 8
SW_RESTORE : c_int : 9
SW_SHOWDEFAULT : c_int : 10
SW_FORCEMINIMIZE : c_int : 11
// SetWindowPos Flags
SWP_NOSIZE :: 0x0001
SWP_NOMOVE :: 0x0002
SWP_NOZORDER :: 0x0004
SWP_NOREDRAW :: 0x0008
SWP_NOACTIVATE :: 0x0010
SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE
SWP_SHOWWINDOW :: 0x0040
SWP_HIDEWINDOW :: 0x0080
SWP_NOCOPYBITS :: 0x0100
SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering
SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING
SWP_DRAWFRAME :: SWP_FRAMECHANGED
SWP_NOREPOSITION :: SWP_NOOWNERZORDER
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
HWND_TOP :: HWND( uintptr(0)) // 0
HWND_BOTTOM :: HWND( uintptr(1)) // 1
HWND_TOPMOST :: HWND(~uintptr(0)) // -1
HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2
// GetSystemMetrics() codes
SM_CXSCREEN :: 0
SM_CYSCREEN :: 1
SM_CXVSCROLL :: 2
SM_CYHSCROLL :: 3
SM_CYCAPTION :: 4
SM_CXBORDER :: 5
SM_CYBORDER :: 6
SM_CXDLGFRAME :: 7
SM_CYDLGFRAME :: 8
SM_CYVTHUMB :: 9
SM_CXHTHUMB :: 10
SM_CXICON :: 11
SM_CYICON :: 12
SM_CXCURSOR :: 13
SM_CYCURSOR :: 14
SM_CYMENU :: 15
SM_CXFULLSCREEN :: 16
SM_CYFULLSCREEN :: 17
SM_CYKANJIWINDOW :: 18
SM_MOUSEPRESENT :: 19
SM_CYVSCROLL :: 20
SM_CXHSCROLL :: 21
SM_DEBUG :: 22
SM_SWAPBUTTON :: 23
SM_RESERVED1 :: 24
SM_RESERVED2 :: 25
SM_RESERVED3 :: 26
SM_RESERVED4 :: 27
SM_CXMIN :: 28
SM_CYMIN :: 29
SM_CXSIZE :: 30
SM_CYSIZE :: 31
SM_CXFRAME :: 32
SM_CYFRAME :: 33
SM_CXMINTRACK :: 34
SM_CYMINTRACK :: 35
SM_CXDOUBLECLK :: 36
SM_CYDOUBLECLK :: 37
SM_CXICONSPACING :: 38
SM_CYICONSPACING :: 39
SM_MENUDROPALIGNMENT :: 40
SM_PENWINDOWS :: 41
SM_DBCSENABLED :: 42
SM_CMOUSEBUTTONS :: 43
SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change
SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change
SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change
SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change
SM_SECURE :: 44
SM_CXEDGE :: 45
SM_CYEDGE :: 46
SM_CXMINSPACING :: 47
SM_CYMINSPACING :: 48
SM_CXSMICON :: 49
SM_CYSMICON :: 50
SM_CYSMCAPTION :: 51
SM_CXSMSIZE :: 52
SM_CYSMSIZE :: 53
SM_CXMENUSIZE :: 54
SM_CYMENUSIZE :: 55
SM_ARRANGE :: 56
SM_CXMINIMIZED :: 57
SM_CYMINIMIZED :: 58
SM_CXMAXTRACK :: 59
SM_CYMAXTRACK :: 60
SM_CXMAXIMIZED :: 61
SM_CYMAXIMIZED :: 62
SM_NETWORK :: 63
SM_CLEANBOOT :: 67
SM_CXDRAG :: 68
SM_CYDRAG :: 69
SM_SHOWSOUNDS :: 70
SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()!
SM_CYMENUCHECK :: 72
SM_SLOWMACHINE :: 73
SM_MIDEASTENABLED :: 74
SM_MOUSEWHEELPRESENT :: 75
SM_XVIRTUALSCREEN :: 76
SM_YVIRTUALSCREEN :: 77
SM_CXVIRTUALSCREEN :: 78
SM_CYVIRTUALSCREEN :: 79
SM_CMONITORS :: 80
SM_SAMEDISPLAYFORMAT :: 81
SM_IMMENABLED :: 82
SM_CXFOCUSBORDER :: 83
SM_CYFOCUSBORDER :: 84
SM_TABLETPC :: 86
SM_MEDIACENTER :: 87
SM_STARTER :: 88
SM_SERVERR2 :: 89
SM_MOUSEHORIZONTALWHEELPRESENT :: 91
SM_CXPADDEDBORDER :: 92
SM_DIGITIZER :: 94
SM_MAXIMUMTOUCHES :: 95
SM_CMETRICS :: 97
SM_REMOTESESSION :: 0x1000
SM_SHUTTINGDOWN :: 0x2000
SM_REMOTECONTROL :: 0x2001
SM_CARETBLINKINGENABLED :: 0x2002
SM_CONVERTIBLESLATEMODE :: 0x2003
SM_SYSTEMDOCKED :: 0x2004
// System Menu Command Values
SC_SIZE :: 0xF000
SC_MOVE :: 0xF010
SC_MINIMIZE :: 0xF020
SC_MAXIMIZE :: 0xF030
SC_NEXTWINDOW :: 0xF040
SC_PREVWINDOW :: 0xF050
SC_CLOSE :: 0xF060
SC_VSCROLL :: 0xF070
SC_HSCROLL :: 0xF080
SC_MOUSEMENU :: 0xF090
SC_KEYMENU :: 0xF100
SC_ARRANGE :: 0xF110
SC_RESTORE :: 0xF120
SC_TASKLIST :: 0xF130
SC_SCREENSAVE :: 0xF140
SC_HOTKEY :: 0xF150
SC_DEFAULT :: 0xF160
SC_MONITORPOWER :: 0xF170
SC_CONTEXTHELP :: 0xF180
SC_SEPARATOR :: 0xF00F
SCF_ISSECURE :: 0x00000001
SC_ICON :: SC_MINIMIZE
SC_ZOOM :: SC_MAXIMIZE
CW_USEDEFAULT : c_int : -2147483648
SIZE_RESTORED :: 0
SIZE_MINIMIZED :: 1
SIZE_MAXIMIZED :: 2
SIZE_MAXSHOW :: 3
SIZE_MAXHIDE :: 4
WMSZ_LEFT :: 1
WMSZ_RIGHT :: 2
WMSZ_TOP :: 3
WMSZ_TOPLEFT :: 4
WMSZ_TOPRIGHT :: 5
WMSZ_BOTTOM :: 6
WMSZ_BOTTOMLEFT :: 7
WMSZ_BOTTOMRIGHT :: 8
// Key State Masks for Mouse Messages
MK_LBUTTON :: 0x0001
MK_RBUTTON :: 0x0002
MK_SHIFT :: 0x0004
MK_CONTROL :: 0x0008
MK_MBUTTON :: 0x0010
MK_XBUTTON1 :: 0x0020
MK_XBUTTON2 :: 0x0040
USER_TIMER_MAXIMUM :: 0x7FFFFFFF
USER_TIMER_MINIMUM :: 0x0000000A
_IDC_APPSTARTING := rawptr(uintptr(32650))
_IDC_ARROW := rawptr(uintptr(32512))
_IDC_CROSS := rawptr(uintptr(32515))
_IDC_HAND := rawptr(uintptr(32649))
_IDC_HELP := rawptr(uintptr(32651))
_IDC_IBEAM := rawptr(uintptr(32513))
_IDC_ICON := rawptr(uintptr(32641))
_IDC_NO := rawptr(uintptr(32648))
_IDC_SIZE := rawptr(uintptr(32640))
_IDC_SIZEALL := rawptr(uintptr(32646))
_IDC_SIZENESW := rawptr(uintptr(32643))
_IDC_SIZENS := rawptr(uintptr(32645))
_IDC_SIZENWSE := rawptr(uintptr(32642))
_IDC_SIZEWE := rawptr(uintptr(32644))
_IDC_UPARROW := rawptr(uintptr(32516))
_IDC_WAIT := rawptr(uintptr(32514))
IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
IDC_ARROW := cstring(_IDC_ARROW)
IDC_CROSS := cstring(_IDC_CROSS)
IDC_HAND := cstring(_IDC_HAND)
IDC_HELP := cstring(_IDC_HELP)
IDC_IBEAM := cstring(_IDC_IBEAM)
IDC_ICON := cstring(_IDC_ICON)
IDC_NO := cstring(_IDC_NO)
IDC_SIZE := cstring(_IDC_SIZE)
IDC_SIZEALL := cstring(_IDC_SIZEALL)
IDC_SIZENESW := cstring(_IDC_SIZENESW)
IDC_SIZENS := cstring(_IDC_SIZENS)
IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
IDC_SIZEWE := cstring(_IDC_SIZEWE)
IDC_UPARROW := cstring(_IDC_UPARROW)
IDC_WAIT := cstring(_IDC_WAIT)
_IDI_APPLICATION := rawptr(uintptr(32512))
_IDI_HAND := rawptr(uintptr(32513))
_IDI_QUESTION := rawptr(uintptr(32514))
_IDI_EXCLAMATION := rawptr(uintptr(32515))
_IDI_ASTERISK := rawptr(uintptr(32516))
_IDI_WINLOGO := rawptr(uintptr(32517))
_IDI_SHIELD := rawptr(uintptr(32518))
IDI_APPLICATION := cstring(_IDI_APPLICATION)
IDI_HAND := cstring(_IDI_HAND)
IDI_QUESTION := cstring(_IDI_QUESTION)
IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION)
IDI_ASTERISK := cstring(_IDI_ASTERISK)
IDI_WINLOGO := cstring(_IDI_WINLOGO)
IDI_SHIELD := cstring(_IDI_SHIELD)
IDI_WARNING := IDI_EXCLAMATION
IDI_ERROR := IDI_HAND
IDI_INFORMATION := IDI_ASTERISK
// DIB color table identifiers
DIB_RGB_COLORS :: 0
DIB_PAL_COLORS :: 1
// constants for CreateDIBitmap
CBM_INIT :: 0x04 // initialize bitmap
// Region Flags
ERROR :: 0
NULLREGION :: 1
SIMPLEREGION :: 2
COMPLEXREGION :: 3
RGN_ERROR :: ERROR
// StretchBlt() Modes
BLACKONWHITE :: 1
WHITEONBLACK :: 2
COLORONCOLOR :: 3
HALFTONE :: 4
MAXSTRETCHBLTMODE :: 4
// Binary raster ops
R2_BLACK :: 1 // 0
R2_NOTMERGEPEN :: 2 // DPon
R2_MASKNOTPEN :: 3 // DPna
R2_NOTCOPYPEN :: 4 // PN
R2_MASKPENNOT :: 5 // PDna
R2_NOT :: 6 // Dn
R2_XORPEN :: 7 // DPx
R2_NOTMASKPEN :: 8 // DPan
R2_MASKPEN :: 9 // DPa
R2_NOTXORPEN :: 10 // DPxn
R2_NOP :: 11 // D
R2_MERGENOTPEN :: 12 // DPno
R2_COPYPEN :: 13 // P
R2_MERGEPENNOT :: 14 // PDno
R2_MERGEPEN :: 15 // DPo
R2_WHITE :: 16 // 1
R2_LAST :: 16
// Ternary raster operations
SRCCOPY : DWORD : 0x00CC0020 // dest = source
SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest
SRCAND : DWORD : 0x008800C6 // dest = source AND dest
SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest
SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest)
NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source)
NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest)
MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern
MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest
PATCOPY : DWORD : 0x00F00021 // dest = pattern
PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo
PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest
DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest)
BLACKNESS : DWORD : 0x00000042 // dest = BLACK
WHITENESS : DWORD : 0x00FF0062 // dest = WHITE
NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call
CAPTUREBLT : DWORD : 0x40000000 // Include layered windows
// Stock Logical Objects
WHITE_BRUSH :: 0
LTGRAY_BRUSH :: 1
GRAY_BRUSH :: 2
DKGRAY_BRUSH :: 3
BLACK_BRUSH :: 4
NULL_BRUSH :: 5
HOLLOW_BRUSH :: NULL_BRUSH
WHITE_PEN :: 6
BLACK_PEN :: 7
NULL_PEN :: 8
OEM_FIXED_FONT :: 10
ANSI_FIXED_FONT :: 11
ANSI_VAR_FONT :: 12
SYSTEM_FONT :: 13
DEVICE_DEFAULT_FONT :: 14
DEFAULT_PALETTE :: 15
SYSTEM_FIXED_FONT :: 16
DEFAULT_GUI_FONT :: 17
DC_BRUSH :: 18
DC_PEN :: 19
STOCK_LAST :: 19
CLR_INVALID :: 0xFFFFFFFF
RGBQUAD :: struct {
rgbBlue: BYTE,
rgbGreen: BYTE,
rgbRed: BYTE,
rgbReserved: BYTE,
}
PIXELFORMATDESCRIPTOR :: struct {
nSize: WORD,
nVersion: WORD,
dwFlags: DWORD,
iPixelType: BYTE,
cColorBits: BYTE,
cRedBits: BYTE,
cRedShift: BYTE,
cGreenBits: BYTE,
cGreenShift: BYTE,
cBlueBits: BYTE,
cBlueShift: BYTE,
cAlphaBits: BYTE,
cAlphaShift: BYTE,
cAccumBits: BYTE,
cAccumRedBits: BYTE,
cAccumGreenBits: BYTE,
cAccumBlueBits: BYTE,
cAccumAlphaBits: BYTE,
cDepthBits: BYTE,
cStencilBits: BYTE,
cAuxBuffers: BYTE,
iLayerType: BYTE,
bReserved: BYTE,
dwLayerMask: DWORD,
dwVisibleMask: DWORD,
dwDamageMask: DWORD,
}
BITMAPINFOHEADER :: struct {
biSize: DWORD,
biWidth: LONG,
biHeight: LONG,
biPlanes: WORD,
biBitCount: WORD,
biCompression: DWORD,
biSizeImage: DWORD,
biXPelsPerMeter: LONG,
biYPelsPerMeter: LONG,
biClrUsed: DWORD,
biClrImportant: DWORD,
}
BITMAPINFO :: struct {
bmiHeader: BITMAPINFOHEADER,
bmiColors: [1]RGBQUAD,
}
// pixel types
PFD_TYPE_RGBA :: 0
PFD_TYPE_COLORINDEX :: 1
// layer types
PFD_MAIN_PLANE :: 0
PFD_OVERLAY_PLANE :: 1
PFD_UNDERLAY_PLANE :: -1
// PIXELFORMATDESCRIPTOR flags
PFD_DOUBLEBUFFER :: 0x00000001
PFD_STEREO :: 0x00000002
PFD_DRAW_TO_WINDOW :: 0x00000004
PFD_DRAW_TO_BITMAP :: 0x00000008
PFD_SUPPORT_GDI :: 0x00000010
PFD_SUPPORT_OPENGL :: 0x00000020
PFD_GENERIC_FORMAT :: 0x00000040
PFD_NEED_PALETTE :: 0x00000080
PFD_NEED_SYSTEM_PALETTE :: 0x00000100
PFD_SWAP_EXCHANGE :: 0x00000200
PFD_SWAP_COPY :: 0x00000400
PFD_SWAP_LAYER_BUFFERS :: 0x00000800
PFD_GENERIC_ACCELERATED :: 0x00001000
PFD_SUPPORT_DIRECTDRAW :: 0x00002000
PFD_DIRECT3D_ACCELERATED :: 0x00004000
PFD_SUPPORT_COMPOSITION :: 0x00008000
// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only
PFD_DEPTH_DONTCARE :: 0x20000000
PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
PFD_STEREO_DONTCARE :: 0x80000000
// constants for the biCompression field
BI_RGB :: 0
BI_RLE8 :: 1
BI_RLE4 :: 2
BI_BITFIELDS :: 3
BI_JPEG :: 4
BI_PNG :: 5
WSA_FLAG_OVERLAPPED: DWORD : 0x01
WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
@@ -379,6 +1069,15 @@ FILE_TYPE_PIPE :: 0x0003
RECT :: struct {left, top, right, bottom: LONG}
POINT :: struct {x, y: LONG}
WINDOWPOS :: struct {
hwnd: HWND,
hwndInsertAfter: HWND,
x: c_int,
y: c_int,
cx: c_int,
cy: c_int,
flags: UINT,
}
when size_of(uintptr) == 4 {
WSADATA :: struct {
@@ -578,7 +1277,7 @@ PROCESS_INFORMATION :: struct {
}
// FYI: This is STARTUPINFOW, not STARTUPINFOA
STARTUPINFO :: struct #packed {
STARTUPINFO :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -784,17 +1483,17 @@ SYSTEM_INFO :: struct {
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
OSVERSIONINFOEXW :: struct {
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
wServicePackMajor: USHORT,
wServicePackMinor: USHORT,
wSuiteMask: USHORT,
wProductType: UCHAR,
wReserved: UCHAR,
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
wServicePackMajor: USHORT,
wServicePackMinor: USHORT,
wSuiteMask: USHORT,
wProductType: UCHAR,
wReserved: UCHAR,
}
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
@@ -837,24 +1536,24 @@ PROFILEINFOW :: struct {
lpDefaultPath: LPWSTR,
lpServerName: LPWSTR,
lpPolicyPath: LPWSTR,
hProfile: HANDLE,
hProfile: HANDLE,
}
// Used in LookupAccountNameW
SID_NAME_USE :: distinct DWORD
SID_TYPE :: enum SID_NAME_USE {
User = 1,
Group,
Domain,
Alias,
WellKnownGroup,
DeletedAccount,
Invalid,
Unknown,
Computer,
Label,
LogonSession,
User = 1,
Group,
Domain,
Alias,
WellKnownGroup,
DeletedAccount,
Invalid,
Unknown,
Computer,
Label,
LogonSession,
}
SECURITY_MAX_SID_SIZE :: 68
@@ -869,7 +1568,7 @@ SID :: struct #packed {
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE)
SID_IDENTIFIER_AUTHORITY :: struct #packed {
Value: [6]u8,
Value: [6]u8,
}
// For NetAPI32
@@ -901,11 +1600,11 @@ USER_INFO_FLAG :: enum DWORD {
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
Normal_Account = 9, // 1 << 9: 0x0200,
InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
Server_Trust_Account = 13, // 1 << 13: 0x2000,
Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
Normal_Account = 9, // 1 << 9: 0x0200,
InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
Server_Trust_Account = 13, // 1 << 13: 0x2000,
}
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG]
@@ -1252,4 +1951,4 @@ SYSTEMTIME :: struct {
minute: WORD,
second: WORD,
milliseconds: WORD,
}
}

View File

@@ -0,0 +1,198 @@
// +build windows
package sys_windows
foreign import user32 "system:User32.lib"
@(default_calling_convention="stdcall")
foreign user32 {
GetClassInfoA :: proc(hInstance: HINSTANCE, lpClassNAme: LPCSTR, lpWndClass: ^WNDCLASSA) -> BOOL ---
GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
GetClassInfoExA :: proc(hInsatnce: HINSTANCE, lpszClass: LPCSTR, lpwcx: ^WNDCLASSEXA) -> BOOL ---
GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
GetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
SetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
GetClassNameA :: proc(hWnd: HWND, lpClassName: LPSTR, nMaxCount: c_int) -> c_int ---
GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
GetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
SetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
RegisterClassA :: proc(lpWndClass: ^WNDCLASSA) -> ATOM ---
RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
RegisterClassExA :: proc(^WNDCLASSEXA) -> ATOM ---
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
CreateWindowExA :: proc(
dwExStyle: DWORD,
lpClassName: LPCSTR,
lpWindowName: LPCSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND ---
CreateWindowExW :: proc(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND ---
DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
GetMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
DispatchMessageA :: proc(lpMsg: ^MSG) -> LRESULT ---
DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostQuitMessage :: proc(nExitCode: c_int) ---
GetQueueStatus :: proc(flags: UINT) -> DWORD ---
DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND ---
FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND ---
FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND ---
FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND ---
LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
SetWindowPos :: proc(
hWnd: HWND,
hWndInsertAfter: HWND,
X: c_int,
Y: c_int,
cx: c_int,
cy: c_int,
uFlags: UINT,
) -> BOOL ---
GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
GetWindowDC :: proc(hWnd: HWND) -> HDC ---
GetDC :: proc(hWnd: HWND) -> HDC ---
ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC ---
EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL ---
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
}
CreateWindowA :: #force_inline proc "stdcall" (
lpClassName: LPCSTR,
lpWindowName: LPCSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND {
return CreateWindowExA(
0,
lpClassName,
lpWindowName,
dwStyle,
X,
Y,
nWidth,
nHeight,
hWndParent,
hMenu,
hInstance,
lpParam,
)
}
CreateWindowW :: #force_inline proc "stdcall" (
lpClassName: LPCTSTR,
lpWindowName: LPCTSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND {
return CreateWindowExW(
0,
lpClassName,
lpWindowName,
dwStyle,
X,
Y,
nWidth,
nHeight,
hWndParent,
hMenu,
hInstance,
lpParam,
)
}
GET_SC_WPARAM :: #force_inline proc(wparam: WPARAM) -> i32 {
return i32(wparam) & 0xFFF0
}

View File

@@ -45,7 +45,9 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
return nil
}
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> string {
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string) {
context.allocator = allocator
if N <= 0 {
return ""
}
@@ -60,7 +62,7 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
text := make([]byte, n+1 if N != -1 else n, allocator)
text := make([]byte, n+1 if N != -1 else n)
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
if n1 == 0 {
@@ -74,7 +76,6 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
break
}
}
return string(text[:n])
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ sema_wait :: proc "contextless" (s: ^Sema) {
win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE)
original_count = s.count
}
if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
@@ -46,7 +46,7 @@ sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration)
}
original_count = s.count
}
if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}

View File

@@ -1,67 +1,75 @@
package thread
/*
thread.Pool
Copyright 2022 eisbehr
Made available under Odin's BSD-3 license.
*/
import "core:intrinsics"
import "core:sync"
import "core:mem"
Task_Status :: enum i32 {
Ready,
Busy,
Waiting,
Term,
}
Task_Proc :: #type proc(task: ^Task)
Task_Proc :: #type proc(task: Task)
Task :: struct {
procedure: Task_Proc,
data: rawptr,
procedure: Task_Proc,
data: rawptr,
user_index: int,
allocator: mem.Allocator,
}
Task_Id :: distinct i32
INVALID_TASK_ID :: Task_Id(-1)
// Do not access the pool's members directly while the pool threads are running,
// since they use different kinds of locking and mutual exclusion devices.
// Careless access can and will lead to nasty bugs. Once initialized, the
// pool's memory address is not allowed to change until it is destroyed.
Pool :: struct {
allocator: mem.Allocator,
mutex: sync.Mutex,
sem_available: sync.Semaphore,
processing_task_count: int, // atomic
is_running: bool,
allocator: mem.Allocator,
mutex: sync.Mutex,
sem_available: sync.Sema,
// the following values are atomic
num_waiting: int,
num_in_processing: int,
num_outstanding: int, // num_waiting + num_in_processing
num_done: int,
// end of atomics
is_running: bool,
threads: []^Thread,
tasks: [dynamic]Task,
tasks: [dynamic]Task,
tasks_done: [dynamic]Task,
}
pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
worker_thread_internal :: proc(t: ^Thread) {
pool := (^Pool)(t.data)
for pool.is_running {
sync.semaphore_wait_for(&pool.sem_available)
if task, ok := pool_try_and_pop_task(pool); ok {
pool_do_work(pool, &task)
}
}
sync.semaphore_post(&pool.sem_available, 1)
}
// Once initialized, the pool's memory address is not allowed to change until
// it is destroyed. If thread_count < 1, thread count 1 will be used.
//
// The thread pool requires an allocator which it either owns, or which is thread safe.
pool_init :: proc(pool: ^Pool, thread_count: int, allocator: mem.Allocator) {
context.allocator = allocator
pool.allocator = allocator
pool.tasks = make([dynamic]Task)
pool.threads = make([]^Thread, thread_count)
pool.tasks = make([dynamic]Task)
pool.tasks_done = make([dynamic]Task)
pool.threads = make([]^Thread, max(thread_count, 1))
sync.mutex_init(&pool.mutex)
sync.semaphore_init(&pool.sem_available)
pool.is_running = true
for _, i in pool.threads {
t := create(worker_thread_internal)
t := create(proc(t: ^Thread) {
pool := (^Pool)(t.data)
for intrinsics.atomic_load(&pool.is_running) {
sync.wait(&pool.sem_available)
if task, ok := pool_pop_waiting(pool); ok {
pool_do_work(pool, task)
}
}
sync.post(&pool.sem_available, 1)
})
t.user_index = i
t.data = pool
pool.threads[i] = t
@@ -70,15 +78,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks)
delete(pool.tasks_done)
for thread in &pool.threads {
destroy(thread)
for t in &pool.threads {
destroy(t)
}
delete(pool.threads, pool.allocator)
sync.mutex_destroy(&pool.mutex)
sync.semaphore_destroy(&pool.sem_available)
}
pool_start :: proc(pool: ^Pool) {
@@ -87,10 +93,12 @@ pool_start :: proc(pool: ^Pool) {
}
}
// Finish tasks that have already started processing, then shut down all pool
// threads. Might leave over waiting tasks, any memory allocated for the
// user data of those tasks will not be freed.
pool_join :: proc(pool: ^Pool) {
pool.is_running = false
sync.semaphore_post(&pool.sem_available, len(pool.threads))
intrinsics.atomic_store(&pool.is_running, false)
sync.post(&pool.sem_available, len(pool.threads))
yield()
@@ -99,53 +107,112 @@ pool_join :: proc(pool: ^Pool) {
}
}
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
sync.mutex_lock(&pool.mutex)
defer sync.mutex_unlock(&pool.mutex)
// Add a task to the thread pool.
//
// Tasks can be added from any thread, not just the thread that created
// the thread pool. You can even add tasks from inside other tasks.
//
// Each task also needs an allocator which it either owns, or which is thread
// safe. By default, allocations in the task are disabled by use of the
// nil_allocator.
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0, allocator := context.allocator) {
sync.guard(&pool.mutex)
task: Task
task.procedure = procedure
task.data = data
task.user_index = user_index
append(&pool.tasks, task)
sync.semaphore_post(&pool.sem_available, 1)
append(&pool.tasks, Task{
procedure = procedure,
data = data,
user_index = user_index,
allocator = allocator,
})
intrinsics.atomic_add(&pool.num_waiting, 1)
intrinsics.atomic_add(&pool.num_outstanding, 1)
sync.post(&pool.sem_available, 1)
}
pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
if sync.mutex_try_lock(&pool.mutex) {
if len(pool.tasks) != 0 {
intrinsics.atomic_add(&pool.processing_task_count, 1)
task = pop_front(&pool.tasks)
got_task = true
}
sync.mutex_unlock(&pool.mutex)
// Number of tasks waiting to be processed. Only informational, mostly for
// debugging. Don't rely on this value being consistent with other num_*
// values.
pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int {
return intrinsics.atomic_load(&pool.num_waiting)
}
// Number of tasks currently being processed. Only informational, mostly for
// debugging. Don't rely on this value being consistent with other num_*
// values.
pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int {
return intrinsics.atomic_load(&pool.num_in_processing)
}
// Outstanding tasks are all tasks that are not done, that is, tasks that are
// waiting, as well as tasks that are currently being processed. Only
// informational, mostly for debugging. Don't rely on this value being
// consistent with other num_* values.
pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int {
return intrinsics.atomic_load(&pool.num_outstanding)
}
// Number of tasks which are done processing. Only informational, mostly for
// debugging. Don't rely on this value being consistent with other num_*
// values.
pool_num_done :: #force_inline proc(pool: ^Pool) -> int {
return intrinsics.atomic_load(&pool.num_done)
}
// If tasks are only being added from one thread, and this procedure is being
// called from that same thread, it will reliably tell if the thread pool is
// empty or not. Empty in this case means there are no tasks waiting, being
// processed, or _done_.
pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0
}
// Mostly for internal use.
pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
sync.guard(&pool.mutex)
if len(pool.tasks) != 0 {
intrinsics.atomic_sub(&pool.num_waiting, 1)
intrinsics.atomic_add(&pool.num_in_processing, 1)
task = pop_front(&pool.tasks)
got_task = true
}
return
}
// Use this to take out finished tasks.
pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
sync.guard(&pool.mutex)
pool_do_work :: proc(pool: ^Pool, task: ^Task) {
task.procedure(task)
intrinsics.atomic_sub(&pool.processing_task_count, 1)
}
pool_wait_and_process :: proc(pool: ^Pool) {
for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
if task, ok := pool_try_and_pop_task(pool); ok {
pool_do_work(pool, &task)
}
// Safety kick
if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
sync.mutex_lock(&pool.mutex)
sync.semaphore_post(&pool.sem_available, len(pool.tasks))
sync.mutex_unlock(&pool.mutex)
}
yield()
if len(pool.tasks_done) != 0 {
task = pop_front(&pool.tasks_done)
got_task = true
intrinsics.atomic_sub(&pool.num_done, 1)
}
return
}
// Mostly for internal use.
pool_do_work :: proc(pool: ^Pool, task: Task) {
{
context.allocator = task.allocator
task.procedure(task)
}
sync.guard(&pool.mutex)
append(&pool.tasks_done, task)
intrinsics.atomic_add(&pool.num_done, 1)
intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1)
}
// Process the rest of the tasks, also use this thread for processing, then join
// all the pool threads.
pool_finish :: proc(pool: ^Pool) {
for task in pool_pop_waiting(pool) {
pool_do_work(pool, task)
}
pool_join(pool)
}

View File

@@ -7,30 +7,19 @@ import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
Thread_State :: enum u8 {
Started,
Joined,
Done,
}
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align 16 {
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
// NOTE: pthread has a proc to query this, but it is marked
// as non-portable ("np") so we do this instead.
done: bool,
// since libpthread doesn't seem to have a way to create a thread
// in a suspended state, we have it wait on this gate, which we
// signal to start it.
// destroyed after thread is started.
start_gate: sync.Condition,
start_mutex: sync.Mutex,
// if true, the thread has been started and the start_gate has been destroyed.
started: bool,
// NOTE: with pthreads, it is undefined behavior for multiple threads
// to call join on the same thread at the same time.
// this value is atomically updated to detect this.
// See the comment in `join`.
already_joined: bool,
cond: sync.Cond,
mutex: sync.Mutex,
flags: bit_set[Thread_State; u8],
}
//
// Creates a thread which will run the given procedure.
@@ -38,26 +27,31 @@ Thread_Os_Specific :: struct #align 16 {
//
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
context = runtime.default_context()
t := (^Thread)(t)
sync.condition_wait_for(&t.start_gate)
sync.condition_destroy(&t.start_gate)
sync.mutex_destroy(&t.start_mutex)
t.start_gate = {}
t.start_mutex = {}
context = t.init_context.? or_else runtime.default_context()
context = runtime.default_context()
sync.lock(&t.mutex)
t.id = sync.current_thread_id()
t.procedure(t)
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
for (.Started not_in t.flags) {
sync.wait(&t.cond, &t.mutex)
}
init_context := t.init_context
context = init_context.? or_else runtime.default_context()
t.procedure(t)
intrinsics.atomic_store(&t.flags, t.flags + { .Done })
sync.unlock(&t.mutex)
if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
intrinsics.atomic_store(&t.done, true)
return nil
}
@@ -76,9 +70,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return nil
}
thread.creation_allocator = context.allocator
sync.mutex_init(&thread.start_mutex)
sync.condition_init(&thread.start_gate, &thread.start_mutex)
// Set thread priority.
policy: i32
@@ -97,65 +88,35 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
res = unix.pthread_attr_setschedparam(&attrs, &params)
assert(res == 0)
thread.procedure = procedure
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread, thread.creation_allocator)
sync.condition_destroy(&thread.start_gate)
sync.mutex_destroy(&thread.start_mutex)
return nil
}
thread.procedure = procedure
return thread
}
_start :: proc(t: ^Thread) {
if intrinsics.atomic_xchg(&t.started, true) {
return
}
sync.condition_signal(&t.start_gate)
sync.guard(&t.mutex)
t.flags += { .Started }
sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
return intrinsics.atomic_load(&t.done)
return .Done in intrinsics.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
// if unix.pthread_self().x == t.unix_thread.x do return;
sync.guard(&t.mutex)
// NOTE(tetra): It's apparently UB for multiple threads to join the same thread
// at the same time.
// If someone else already did, spin until the thread dies.
// See note on `already_joined` field.
// TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
// sure it makes sense to need to join from multiple threads?
if intrinsics.atomic_xchg(&t.already_joined, true) {
for {
if intrinsics.atomic_load(&t.done) {
return
}
intrinsics.cpu_relax()
}
}
// NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
// will just return 3 (ESRCH).
// We do this instead because I don't know if there is a danger
// that you may join a different thread from the one you called join on,
// if the thread handle is reused.
if intrinsics.atomic_load(&t.done) {
if .Joined in t.flags || unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
ret_val: rawptr
_ = unix.pthread_join(t.unix_thread, &ret_val)
if !intrinsics.atomic_load(&t.done) {
panic("thread not done after join")
}
unix.pthread_join(t.unix_thread, nil)
t.flags += { .Joined }
}
_join_multiple :: proc(threads: ..^Thread) {
@@ -164,14 +125,12 @@ _join_multiple :: proc(threads: ..^Thread) {
}
}
_destroy :: proc(t: ^Thread) {
_join(t)
t.unix_thread = {}
free(t, t.creation_allocator)
}
_terminate :: proc(t: ^Thread, exit_code: int) {
// TODO(bill)
}

View File

@@ -3,13 +3,21 @@
package thread
import "core:runtime"
import sync "core:sync/sync2"
import "core:intrinsics"
import "core:sync"
import win32 "core:sys/windows"
Thread_State :: enum u8 {
Started,
Joined,
Done,
}
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
done: bool, // see note in `is_done`
mutex: sync.Mutex,
flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
context = t.init_context.? or_else runtime.default_context()
t.id = sync.current_thread_id()
t.procedure(t)
intrinsics.atomic_store(&t.flags, t.flags + {.Done})
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
}
sync.atomic_store(&t.done, true)
return 0
}
@@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return thread
}
_start :: proc(thread: ^Thread) {
win32.ResumeThread(thread.win32_thread)
_start :: proc(t: ^Thread) {
sync.guard(&t.mutex)
t.flags += {.Started}
win32.ResumeThread(t.win32_thread)
}
_is_done :: proc(using thread: ^Thread) -> bool {
_is_done :: proc(t: ^Thread) -> bool {
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
return sync.atomic_load(&done)
return .Done in sync.atomic_load(&t.flags)
}
_join :: proc(using thread: ^Thread) {
if win32_thread != win32.INVALID_HANDLE {
win32.WaitForSingleObject(win32_thread, win32.INFINITE)
win32.CloseHandle(win32_thread)
win32_thread = win32.INVALID_HANDLE
_join :: proc(t: ^Thread) {
sync.guard(&t.mutex)
if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE {
return
}
win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
win32.CloseHandle(t.win32_thread)
t.win32_thread = win32.INVALID_HANDLE
t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {

View File

@@ -16,140 +16,140 @@ String :: struct {
}
@(private)
_len :: builtin.len; // helper procedure
_len :: builtin.len // helper procedure
init :: proc(s: ^String, contents: string) -> ^String {
s.contents = contents;
s.byte_pos = 0;
s.rune_pos = 0;
s.contents = contents
s.byte_pos = 0
s.rune_pos = 0
for i in 0..<_len(contents) {
if contents[i] >= utf8.RUNE_SELF {
s.rune_count = utf8.rune_count_in_string(contents);
_, s.width = utf8.decode_rune_in_string(contents);
s.non_ascii = i;
return s;
s.rune_count = utf8.rune_count_in_string(contents)
_, s.width = utf8.decode_rune_in_string(contents)
s.non_ascii = i
return s
}
}
s.rune_count = _len(contents);
s.width = 0;
s.non_ascii = _len(contents);
return s;
s.rune_count = _len(contents)
s.width = 0
s.non_ascii = _len(contents)
return s
}
to_string :: proc(s: ^String) -> string {
return s.contents;
return s.contents
}
len :: proc(s: ^String) -> int {
return s.rune_count;
return s.rune_count
}
is_ascii :: proc(s: ^String) -> bool {
return s.width == 0;
return s.width == 0
}
at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
runtime.bounds_check_error_loc(loc, i, s.rune_count);
runtime.bounds_check_error_loc(loc, i, s.rune_count)
if i < s.non_ascii {
return rune(s.contents[i]);
return rune(s.contents[i])
}
switch i {
case 0:
r, s.width = utf8.decode_rune_in_string(s.contents);
s.rune_pos = 0;
s.byte_pos = 0;
return;
r, s.width = utf8.decode_rune_in_string(s.contents)
s.rune_pos = 0
s.byte_pos = 0
return
case s.rune_count-1:
r, s.width = utf8.decode_rune_in_string(s.contents);
s.rune_pos = i;
s.byte_pos = _len(s.contents) - s.width;
return;
r, s.width = utf8.decode_rune_in_string(s.contents)
s.rune_pos = i
s.byte_pos = _len(s.contents) - s.width
return
case s.rune_pos-1:
r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
s.rune_pos = i;
s.byte_pos -= s.width;
return;
r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos])
s.rune_pos = i
s.byte_pos -= s.width
return
case s.rune_pos+1:
s.rune_pos = i;
s.byte_pos += s.width;
fallthrough;
s.rune_pos = i
s.byte_pos += s.width
fallthrough
case s.rune_pos:
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
return;
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
return
}
// Linear scan
scan_forward := true;
scan_forward := true
if i < s.rune_pos {
if i < (s.rune_pos-s.non_ascii)/2 {
s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii
} else {
scan_forward = false;
scan_forward = false
}
} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
// scan_forward = true;
// scan_forward = true
} else {
s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
scan_forward = false;
s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count
scan_forward = false
}
if scan_forward {
for {
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
if s.rune_pos == i {
return;
return
}
s.rune_pos += 1;
s.byte_pos += s.width;
s.rune_pos += 1
s.byte_pos += s.width
}
} else {
for {
r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
s.rune_pos -= 1;
s.byte_pos -= s.width;
r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos])
s.rune_pos -= 1
s.byte_pos -= s.width
if s.rune_pos == i {
return;
return
}
}
}
}
slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count)
if j < s.non_ascii {
return s.contents[i:j];
return s.contents[i:j]
}
if i == j {
return "";
return ""
}
lo, hi: int;
lo, hi: int
if i < s.non_ascii {
lo = i;
lo = i
} else if i == s.rune_count {
lo = _len(s.contents);
lo = _len(s.contents)
} else {
at(s, i, loc);
lo = s.byte_pos;
at(s, i, loc)
lo = s.byte_pos
}
if j == s.rune_count {
hi = _len(s.contents);
hi = _len(s.contents)
} else {
at(s, j, loc);
hi = s.byte_pos;
at(s, j, loc)
hi = s.byte_pos
}
return s.contents[lo:hi];
return s.contents[lo:hi]
}

View File

@@ -96,7 +96,6 @@ import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
import sync2 "core:sync/sync2"
import testing "core:testing"
import scanner "core:text/scanner"
import thread "core:thread"
@@ -104,6 +103,7 @@ import time "core:time"
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
main :: proc(){}
@@ -186,11 +186,11 @@ _ :: sort
_ :: strconv
_ :: strings
_ :: sync
_ :: sync2
_ :: testing
_ :: scanner
_ :: thread
_ :: time
_ :: unicode
_ :: utf8
_ :: utf8string
_ :: utf16

View File

@@ -1107,11 +1107,6 @@ prefix_table := [?]string{
}
threading_example :: proc() {
if ODIN_OS == .Darwin {
// TODO: Fix threads on darwin/macOS
return
}
fmt.println("\n# threading_example")
{ // Basic Threads
@@ -1152,7 +1147,7 @@ threading_example :: proc() {
{ // Thread Pool
fmt.println("\n## Thread Pool")
task_proc :: proc(t: ^thread.Task) {
task_proc :: proc(t: thread.Task) {
index := t.user_index % len(prefix_table)
for iteration in 1..=5 {
fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration)
@@ -1162,7 +1157,7 @@ threading_example :: proc() {
}
pool: thread.Pool
thread.pool_init(pool=&pool, thread_count=3)
thread.pool_init(pool=&pool, thread_count=3, allocator=context.allocator)
defer thread.pool_destroy(&pool)
@@ -1171,7 +1166,7 @@ threading_example :: proc() {
}
thread.pool_start(&pool)
thread.pool_wait_and_process(&pool)
thread.pool_finish(&pool)
}
}

View File

@@ -304,7 +304,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
} else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
@@ -379,6 +379,35 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
}
}
bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
Operand x = {};
check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
if (x.mode == Addressing_Invalid) {
return false;
}
if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) {
gbString str = type_to_string(x.type);
if (extra_message) {
error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str);
} else {
error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str);
}
gb_string_free(str);
return false;
}
i64 value = exact_value_to_i64(x.value);
if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) {
error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value);
return false;
}
if (memory_order_) {
*memory_order_ = cast(OdinAtomicMemoryOrder)value;
}
return true;
}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
if (ce->inlining != ProcInlining_none) {
@@ -423,6 +452,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
case BuiltinProc_atomic_thread_fence:
case BuiltinProc_atomic_signal_fence:
// NOTE(bill): first type will require a type hint
break;
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -3198,11 +3232,27 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
case BuiltinProc_atomic_fence_acqrel:
operand->mode = Addressing_NoValue;
case BuiltinProc_atomic_thread_fence:
case BuiltinProc_atomic_signal_fence:
{
OdinAtomicMemoryOrder memory_order = {};
if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) {
return false;
}
switch (memory_order) {
case OdinAtomicMemoryOrder_acquire:
case OdinAtomicMemoryOrder_release:
case OdinAtomicMemoryOrder_acq_rel:
case OdinAtomicMemoryOrder_seq_cst:
break;
default:
error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]);
break;
}
operand->mode = Addressing_NoValue;
}
break;
case BuiltinProc_volatile_store:
@@ -3210,9 +3260,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_unaligned_store:
/*fallthrough*/
case BuiltinProc_atomic_store:
case BuiltinProc_atomic_store_rel:
case BuiltinProc_atomic_store_relaxed:
case BuiltinProc_atomic_store_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3228,14 +3275,40 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_atomic_store_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
return false;
}
Operand x = {};
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_assignment(c, &x, elem, builtin_name);
OdinAtomicMemoryOrder memory_order = {};
if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) {
return false;
}
switch (memory_order) {
case OdinAtomicMemoryOrder_consume:
case OdinAtomicMemoryOrder_acquire:
case OdinAtomicMemoryOrder_acq_rel:
error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
break;
}
operand->type = nullptr;
operand->mode = Addressing_NoValue;
break;
}
case BuiltinProc_volatile_load:
/*fallthrough*/
case BuiltinProc_unaligned_load:
/*fallthrough*/
case BuiltinProc_atomic_load:
case BuiltinProc_atomic_load_acq:
case BuiltinProc_atomic_load_relaxed:
case BuiltinProc_atomic_load_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3247,41 +3320,38 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_atomic_load_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
return false;
}
OdinAtomicMemoryOrder memory_order = {};
if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
return false;
}
switch (memory_order) {
case OdinAtomicMemoryOrder_release:
case OdinAtomicMemoryOrder_acq_rel:
error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
break;
}
operand->type = elem;
operand->mode = Addressing_Value;
break;
}
case BuiltinProc_atomic_add:
case BuiltinProc_atomic_add_acq:
case BuiltinProc_atomic_add_rel:
case BuiltinProc_atomic_add_acqrel:
case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
case BuiltinProc_atomic_sub_acq:
case BuiltinProc_atomic_sub_rel:
case BuiltinProc_atomic_sub_acqrel:
case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
case BuiltinProc_atomic_and_acq:
case BuiltinProc_atomic_and_rel:
case BuiltinProc_atomic_and_acqrel:
case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
case BuiltinProc_atomic_nand_acq:
case BuiltinProc_atomic_nand_rel:
case BuiltinProc_atomic_nand_acqrel:
case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
case BuiltinProc_atomic_or_acq:
case BuiltinProc_atomic_or_rel:
case BuiltinProc_atomic_or_acqrel:
case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
case BuiltinProc_atomic_xor_acq:
case BuiltinProc_atomic_xor_rel:
case BuiltinProc_atomic_xor_acqrel:
case BuiltinProc_atomic_xor_relaxed:
case BuiltinProc_atomic_xchg:
case BuiltinProc_atomic_xchg_acq:
case BuiltinProc_atomic_xchg_rel:
case BuiltinProc_atomic_xchg_acqrel:
case BuiltinProc_atomic_xchg_relaxed:
case BuiltinProc_atomic_exchange:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3297,25 +3367,35 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_atomic_cxchg:
case BuiltinProc_atomic_cxchg_acq:
case BuiltinProc_atomic_cxchg_rel:
case BuiltinProc_atomic_cxchg_acqrel:
case BuiltinProc_atomic_cxchg_relaxed:
case BuiltinProc_atomic_cxchg_failrelaxed:
case BuiltinProc_atomic_cxchg_failacq:
case BuiltinProc_atomic_cxchg_acq_failrelaxed:
case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
case BuiltinProc_atomic_add_explicit:
case BuiltinProc_atomic_sub_explicit:
case BuiltinProc_atomic_and_explicit:
case BuiltinProc_atomic_nand_explicit:
case BuiltinProc_atomic_or_explicit:
case BuiltinProc_atomic_xor_explicit:
case BuiltinProc_atomic_exchange_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
return false;
}
Operand x = {};
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_assignment(c, &x, elem, builtin_name);
case BuiltinProc_atomic_cxchgweak:
case BuiltinProc_atomic_cxchgweak_acq:
case BuiltinProc_atomic_cxchgweak_rel:
case BuiltinProc_atomic_cxchgweak_acqrel:
case BuiltinProc_atomic_cxchgweak_relaxed:
case BuiltinProc_atomic_cxchgweak_failrelaxed:
case BuiltinProc_atomic_cxchgweak_failacq:
case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
return false;
}
operand->type = elem;
operand->mode = Addressing_Value;
break;
}
case BuiltinProc_atomic_compare_exchange_strong:
case BuiltinProc_atomic_compare_exchange_weak:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3333,7 +3413,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = elem;
break;
}
break;
case BuiltinProc_atomic_compare_exchange_strong_explicit:
case BuiltinProc_atomic_compare_exchange_weak_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
return false;
}
Operand x = {};
Operand y = {};
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_expr_with_type_hint(c, &y, ce->args[2], elem);
check_assignment(c, &x, elem, builtin_name);
check_assignment(c, &y, elem, builtin_name);
OdinAtomicMemoryOrder success_memory_order = {};
OdinAtomicMemoryOrder failure_memory_order = {};
if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) {
return false;
}
if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) {
return false;
}
bool invalid_combination = false;
switch (success_memory_order) {
case OdinAtomicMemoryOrder_relaxed:
case OdinAtomicMemoryOrder_release:
if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) {
invalid_combination = true;
}
break;
case OdinAtomicMemoryOrder_consume:
switch (failure_memory_order) {
case OdinAtomicMemoryOrder_relaxed:
case OdinAtomicMemoryOrder_consume:
break;
default:
invalid_combination = true;
break;
}
break;
case OdinAtomicMemoryOrder_acquire:
case OdinAtomicMemoryOrder_acq_rel:
switch (failure_memory_order) {
case OdinAtomicMemoryOrder_relaxed:
case OdinAtomicMemoryOrder_consume:
case OdinAtomicMemoryOrder_acquire:
break;
default:
invalid_combination = true;
break;
}
break;
case OdinAtomicMemoryOrder_seq_cst:
switch (failure_memory_order) {
case OdinAtomicMemoryOrder_relaxed:
case OdinAtomicMemoryOrder_consume:
case OdinAtomicMemoryOrder_acquire:
case OdinAtomicMemoryOrder_seq_cst:
break;
default:
invalid_combination = true;
break;
}
break;
default:
invalid_combination = true;
break;
}
if (invalid_combination) {
error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s",
LIT(builtin_name),
OdinAtomicMemoryOrder_strings[success_memory_order],
OdinAtomicMemoryOrder_strings[failure_memory_order]
);
}
operand->mode = Addressing_OptionalOk;
operand->type = elem;
break;
}
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
@@ -4111,6 +4276,73 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
case BuiltinProc_wasm_memory_grow:
{
if (!is_arch_wasm()) {
error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
return false;
}
Operand index = {};
Operand delta = {};
check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false;
convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false;
if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
gbString e = expr_to_string(index.expr);
gbString t = type_to_string(index.type);
error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) {
gbString e = expr_to_string(delta.expr);
gbString t = type_to_string(delta.type);
error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
operand->mode = Addressing_Value;
operand->type = t_int;
operand->value = {};
break;
}
break;
case BuiltinProc_wasm_memory_size:
{
if (!is_arch_wasm()) {
error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
return false;
}
Operand index = {};
check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
gbString e = expr_to_string(index.expr);
gbString t = type_to_string(index.type);
error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
operand->mode = Addressing_Value;
operand->type = t_int;
operand->value = {};
break;
}
break;
}
return true;

View File

@@ -1137,7 +1137,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
error(e->token, "@(thread_local) is not supported for this target platform");
e->Variable.thread_local_model.len = 0;
// NOTE(bill): ignore this message for the time begin
// error(e->token, "@(thread_local) is not supported for this target platform");
}
String context_name = str_lit("variable declaration");

View File

@@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
proc_type = base_type(proc_type);
TypeProc *pt = &proc_type->Proc;
isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
GB_ASSERT(pt != nullptr);
GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
bool variadic = pt->variadic;
bool vari_expand = (ce->ellipsis.pos.line != 0);
i64 score = 0;
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
if (pt->params != nullptr) {
param_tuple = &pt->params->Tuple;
if (pt->Proc.params != nullptr) {
param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
if (param_count_) *param_count_ = param_count;
return param_count_excluding_defaults;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
proc_type = base_type(proc_type);
TypeProc *pt = &proc_type->Proc;
isize param_count = 0;
isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, &param_count);
bool variadic = pt->variadic;
bool vari_expand = (ce->ellipsis.pos.line != 0);
i64 score = 0;
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
TypeTuple *param_tuple = nullptr;
if (pt->params != nullptr) {
param_tuple = &pt->params->Tuple;
}
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
Array<Entity *> procs = proc_group_entities(c, *operand);
auto procs = proc_group_entities_cloned(c, *operand);
if (procs.count > 1) {
isize max_arg_count = args.count;
for_array(i, args) {
// NOTE(bill): The only thing that may have multiple values
// will be a call expression (assuming `or_return` and `()` will be stripped)
Ast *arg = strip_or_return_expr(args[i]);
if (arg && arg->kind == Ast_CallExpr) {
max_arg_count = ISIZE_MAX;
break;
}
}
for (isize proc_index = 0; proc_index < procs.count; /**/) {
Entity *proc = procs[proc_index];
Type *pt = base_type(proc->type);
if (!(pt != nullptr && is_type_proc(pt))) {
continue;
}
isize param_count = 0;
isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
if (param_count_excluding_defaults > max_arg_count) {
array_unordered_remove(&procs, proc_index);
} else {
proc_index++;
}
}
}
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -8953,6 +9000,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Invalid;
o->type = t_invalid;
o->value = {ExactValue_Invalid};
switch (node->kind) {
default:
@@ -9999,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
if (st->maybe) str = gb_string_appendc(str, "#maybe ");
switch (st->kind) {
case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
}
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);

View File

@@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
}
if (ok) {
array_add(&variants, t);
if (ut->kind == UnionType_shared_nil) {
if (!type_has_nil(t)) {
gbString s = type_to_string(t);
error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
gb_string_free(s);
}
}
}
}
}
union_type->Union.variants = slice_from_array(variants);
union_type->Union.no_nil = ut->no_nil;
union_type->Union.maybe = ut->maybe;
if (union_type->Union.no_nil) {
union_type->Union.kind = ut->kind;
switch (ut->kind) {
case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
}
if (union_type->Union.maybe) {
break;
case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
break;
}
if (ut->align != nullptr) {
@@ -1960,20 +1969,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
if (param_count > 0) {
for_array(i, params->Tuple.variables) {
Entity *param = params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
ParameterValue pv = param->Variable.param_value;
if (pv.kind == ParameterValue_Constant &&
pv.value.kind == ExactValue_Procedure) {
type->Proc.has_proc_default_values = true;
break;
}
}
}
}
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";

View File

@@ -829,15 +829,16 @@ struct GlobalEnumValue {
i64 value;
};
Slice<Entity *> add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count) {
Slice<Entity *> add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) {
Scope *scope = create_scope(nullptr, builtin_pkg->scope);
Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
Type *enum_type = alloc_type_enum();
Type *named_type = alloc_type_named(type_name, enum_type, e);
Type *named_type = alloc_type_named(type_name, enum_type, entity);
set_base_type(named_type, enum_type);
enum_type->Enum.base_type = t_int;
enum_type->Enum.scope = scope;
entity->type = named_type;
auto fields = array_make<Entity *>(permanent_allocator(), value_count);
for (isize i = 0; i < value_count; i++) {
@@ -858,6 +859,9 @@ Slice<Entity *> add_global_enum_type(String const &type_name, GlobalEnumValue *v
enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value;
enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value;
if (enum_type_) *enum_type_ = named_type;
return slice_from_array(fields);
}
void add_global_enum_constant(Slice<Entity *> const &fields, char const *name, i64 value) {
@@ -986,6 +990,21 @@ void init_universal(void) {
add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
}
{
GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = {
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed},
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume},
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire},
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release},
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel},
{OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst},
};
add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order);
GB_ASSERT(t_atomic_memory_order->kind == Type_Named);
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
}
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
@@ -2657,6 +2676,15 @@ Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) {
return procs;
}
Array<Entity *> proc_group_entities_cloned(CheckerContext *c, Operand o) {
auto entities = proc_group_entities(c, o);
if (entities.count == 0) {
return {};
}
return array_clone(permanent_allocator(), entities);
}
void init_core_type_info(Checker *c) {

View File

@@ -86,77 +86,30 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
BuiltinProc_atomic_fence,
BuiltinProc_atomic_fence_acq,
BuiltinProc_atomic_fence_rel,
BuiltinProc_atomic_fence_acqrel,
BuiltinProc_atomic_thread_fence,
BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
BuiltinProc_atomic_store_rel,
BuiltinProc_atomic_store_relaxed,
BuiltinProc_atomic_store_unordered,
BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
BuiltinProc_atomic_load_acq,
BuiltinProc_atomic_load_relaxed,
BuiltinProc_atomic_load_unordered,
BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
BuiltinProc_atomic_add_acq,
BuiltinProc_atomic_add_rel,
BuiltinProc_atomic_add_acqrel,
BuiltinProc_atomic_add_relaxed,
BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
BuiltinProc_atomic_sub_acq,
BuiltinProc_atomic_sub_rel,
BuiltinProc_atomic_sub_acqrel,
BuiltinProc_atomic_sub_relaxed,
BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
BuiltinProc_atomic_and_acq,
BuiltinProc_atomic_and_rel,
BuiltinProc_atomic_and_acqrel,
BuiltinProc_atomic_and_relaxed,
BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
BuiltinProc_atomic_nand_acq,
BuiltinProc_atomic_nand_rel,
BuiltinProc_atomic_nand_acqrel,
BuiltinProc_atomic_nand_relaxed,
BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
BuiltinProc_atomic_or_acq,
BuiltinProc_atomic_or_rel,
BuiltinProc_atomic_or_acqrel,
BuiltinProc_atomic_or_relaxed,
BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
BuiltinProc_atomic_xor_acq,
BuiltinProc_atomic_xor_rel,
BuiltinProc_atomic_xor_acqrel,
BuiltinProc_atomic_xor_relaxed,
BuiltinProc_atomic_xchg,
BuiltinProc_atomic_xchg_acq,
BuiltinProc_atomic_xchg_rel,
BuiltinProc_atomic_xchg_acqrel,
BuiltinProc_atomic_xchg_relaxed,
BuiltinProc_atomic_cxchg,
BuiltinProc_atomic_cxchg_acq,
BuiltinProc_atomic_cxchg_rel,
BuiltinProc_atomic_cxchg_acqrel,
BuiltinProc_atomic_cxchg_relaxed,
BuiltinProc_atomic_cxchg_failrelaxed,
BuiltinProc_atomic_cxchg_failacq,
BuiltinProc_atomic_cxchg_acq_failrelaxed,
BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
BuiltinProc_atomic_cxchgweak,
BuiltinProc_atomic_cxchgweak_acq,
BuiltinProc_atomic_cxchgweak_rel,
BuiltinProc_atomic_cxchgweak_acqrel,
BuiltinProc_atomic_cxchgweak_relaxed,
BuiltinProc_atomic_cxchgweak_failrelaxed,
BuiltinProc_atomic_cxchgweak_failacq,
BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
BuiltinProc_atomic_xor_explicit,
BuiltinProc_atomic_exchange,
BuiltinProc_atomic_exchange_explicit,
BuiltinProc_atomic_compare_exchange_strong,
BuiltinProc_atomic_compare_exchange_strong_explicit,
BuiltinProc_atomic_compare_exchange_weak,
BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -260,6 +213,8 @@ BuiltinProc__type_end,
BuiltinProc_constant_utf16_cstring,
BuiltinProc_wasm_memory_grow,
BuiltinProc_wasm_memory_size,
BuiltinProc_COUNT,
};
@@ -350,78 +305,30 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -523,4 +430,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};

View File

@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {

View File

@@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
switch (type->Union.kind) {
case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break;
case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
}
{
auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));

View File

@@ -115,6 +115,16 @@ struct ParameterValue {
};
};
bool has_parameter_value(ParameterValue const &param_value) {
if (param_value.kind != ParameterValue_Invalid) {
return true;
}
if (param_value.original_ast_expr != nullptr) {
return true;
}
return false;
}
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};

View File

@@ -3028,7 +3028,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
lbValue cond = lb_build_cond(p, te->cond, then, else_);
lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
@@ -4646,7 +4646,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
lbValue cond = lb_build_cond(p, te->cond, then, else_);
lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));

View File

@@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
Type *pt = base_type(type_deref(parent.type));
GB_ASSERT(pt->kind == Type_Union);
if (pt->Union.kind == UnionType_shared_nil) {
lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
lbBlock *done = lb_create_block(p, "shared_nil.done");
lb_emit_store(p, underlying, variant);
lb_emit_store_union_variant_tag(p, parent, variant_type);
lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
lb_start_block(p, if_nil);
lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
lb_emit_jump(p, done);
lb_start_block(p, if_not_nil);
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
lb_emit_store(p, underlying, variant);
lb_emit_store_union_variant_tag(p, parent, variant_type);
lb_emit_jump(p, done);
lb_start_block(p, done);
} else {
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
lb_emit_store(p, underlying, variant);
lb_emit_store_union_variant_tag(p, parent, variant_type);
}
}
@@ -2312,7 +2337,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2354,7 +2379,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2590,7 +2615,7 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g.value, LLVMInternalLinkage);
LLVMSetLinkage(g.value, LLVMPrivateLinkage);
string_map_set(&m->members, s, g);
return g;
}
@@ -2602,6 +2627,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
// Use to signal not to do compile time short circuit for consts
lbValue no_comptime_short_circuit = {};
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2609,7 +2637,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
return lb_build_cond(p, ue->expr, false_block, true_block);
lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
if (cond_val.value && LLVMIsConstant(cond_val.value)) {
return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
}
return no_comptime_short_circuit;
}
case_end;
@@ -2618,12 +2650,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
return lb_build_cond(p, be->right, true_block, false_block);
lb_build_cond(p, be->right, true_block, false_block);
return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
return lb_build_cond(p, be->right, true_block, false_block);
lb_build_cond(p, be->right, true_block, false_block);
return no_comptime_short_circuit;
}
case_end;
}

View File

@@ -1372,12 +1372,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
if (build_context.metrics.arch == TargetArch_i386 ||
build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {});
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {});
// NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield`
// See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else {
// NOTE: default to something to prevent optimization
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
@@ -1598,36 +1606,26 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_atomic_fence:
LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
// TODO(bill): Which is correct?
case BuiltinProc_atomic_thread_fence:
LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, "");
return {};
case BuiltinProc_atomic_fence_acq:
LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
return {};
case BuiltinProc_atomic_fence_rel:
LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
return {};
case BuiltinProc_atomic_fence_acqrel:
LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
case BuiltinProc_atomic_signal_fence:
LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, "");
return {};
case BuiltinProc_volatile_store:
case BuiltinProc_atomic_store:
case BuiltinProc_atomic_store_rel:
case BuiltinProc_atomic_store_relaxed:
case BuiltinProc_atomic_store_unordered: {
case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break;
case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1637,18 +1635,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_volatile_load:
case BuiltinProc_atomic_load:
case BuiltinProc_atomic_load_acq:
case BuiltinProc_atomic_load_relaxed:
case BuiltinProc_atomic_load_unordered: {
case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break;
case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1678,40 +1672,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_atomic_add:
case BuiltinProc_atomic_add_acq:
case BuiltinProc_atomic_add_rel:
case BuiltinProc_atomic_add_acqrel:
case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
case BuiltinProc_atomic_sub_acq:
case BuiltinProc_atomic_sub_rel:
case BuiltinProc_atomic_sub_acqrel:
case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
case BuiltinProc_atomic_and_acq:
case BuiltinProc_atomic_and_rel:
case BuiltinProc_atomic_and_acqrel:
case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
case BuiltinProc_atomic_nand_acq:
case BuiltinProc_atomic_nand_rel:
case BuiltinProc_atomic_nand_acqrel:
case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
case BuiltinProc_atomic_or_acq:
case BuiltinProc_atomic_or_rel:
case BuiltinProc_atomic_or_acqrel:
case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
case BuiltinProc_atomic_xor_acq:
case BuiltinProc_atomic_xor_rel:
case BuiltinProc_atomic_xor_acqrel:
case BuiltinProc_atomic_xor_relaxed:
case BuiltinProc_atomic_xchg:
case BuiltinProc_atomic_xchg_acq:
case BuiltinProc_atomic_xchg_rel:
case BuiltinProc_atomic_xchg_acqrel:
case BuiltinProc_atomic_xchg_relaxed: {
case BuiltinProc_atomic_exchange:
case BuiltinProc_atomic_add_explicit:
case BuiltinProc_atomic_sub_explicit:
case BuiltinProc_atomic_and_explicit:
case BuiltinProc_atomic_nand_explicit:
case BuiltinProc_atomic_or_explicit:
case BuiltinProc_atomic_xor_explicit:
case BuiltinProc_atomic_exchange_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
@@ -1720,41 +1693,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMAtomicOrdering ordering = {};
switch (id) {
case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
}
lbValue res = {};
@@ -1763,24 +1715,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
case BuiltinProc_atomic_cxchg:
case BuiltinProc_atomic_cxchg_acq:
case BuiltinProc_atomic_cxchg_rel:
case BuiltinProc_atomic_cxchg_acqrel:
case BuiltinProc_atomic_cxchg_relaxed:
case BuiltinProc_atomic_cxchg_failrelaxed:
case BuiltinProc_atomic_cxchg_failacq:
case BuiltinProc_atomic_cxchg_acq_failrelaxed:
case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
case BuiltinProc_atomic_cxchgweak:
case BuiltinProc_atomic_cxchgweak_acq:
case BuiltinProc_atomic_cxchgweak_rel:
case BuiltinProc_atomic_cxchgweak_acqrel:
case BuiltinProc_atomic_cxchgweak_relaxed:
case BuiltinProc_atomic_cxchgweak_failrelaxed:
case BuiltinProc_atomic_cxchgweak_failacq:
case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: {
case BuiltinProc_atomic_compare_exchange_strong:
case BuiltinProc_atomic_compare_exchange_weak:
case BuiltinProc_atomic_compare_exchange_strong_explicit:
case BuiltinProc_atomic_compare_exchange_weak_explicit: {
lbValue address = lb_build_expr(p, ce->args[0]);
Type *elem = type_deref(address.type);
lbValue old_value = lb_build_expr(p, ce->args[1]);
@@ -1793,28 +1731,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMBool weak = false;
switch (id) {
case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break;
case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break;
case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break;
case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
}
// TODO(bill): Figure out how to make it weak
LLVMBool single_threaded = weak;
LLVMBool single_threaded = false;
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
p->builder, address.value,
@@ -1823,6 +1747,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
failure_ordering,
single_threaded
);
LLVMSetWeak(value, weak);
if (tv.type->kind == Type_Tuple) {
Type *fix_typed = alloc_type_tuple();
@@ -2199,6 +2124,45 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
case BuiltinProc_wasm_memory_grow:
{
char const *name = "llvm.wasm.memory.grow";
LLVMTypeRef types[1] = {
lb_type(p->module, t_uintptr),
};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[2] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
case BuiltinProc_wasm_memory_size:
{
char const *name = "llvm.wasm.memory.size";
LLVMTypeRef types[1] = {
lb_type(p->module, t_uintptr),
};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
LLVMValueRef args[1] = {};
args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
lbValue res = {};
res.type = tv.type;
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
return res;
}
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));

View File

@@ -1652,13 +1652,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
// Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
// will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
if (LLVMIsConstant(cond.value)) {
if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.

View File

@@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
{
LLVMValueRef vals[7] = {};
LLVMValueRef vals[8] = {};
isize variant_count = gb_max(0, t->Union.variants.count);
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
@@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value;
vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
for (isize i = 0; i < gb_count_of(vals); i++) {
if (vals[i] == nullptr) {

View File

@@ -2005,3 +2005,25 @@ lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
}
LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
switch (v) {
case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
}
GB_PANIC("Unknown atomic ordering");
return LLVMAtomicOrderingSequentiallyConsistent;
}
LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
ExactValue value = type_and_value_of_expr(expr).value;
return llvm_atomic_ordering_from_odin(value);
}

View File

@@ -117,6 +117,9 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
if (WIFEXITED(exit_code)) {
exit_code = WEXITSTATUS(exit_code);
}
#endif
if (exit_code) {
@@ -433,6 +436,10 @@ i32 linker_stage(lbGenerator *gen) {
String output_ext = {};
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
if (build_context.no_crt) {
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
}
// NOTE(dweiler): We use clang as a frontend for the linker as there are
// other runtime and compiler support libraries that need to be linked in
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.

View File

@@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_c
}
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
result->UnionType.variants = slice_from_array(variants);
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
result->UnionType.no_nil = no_nil;
result->UnionType.maybe = maybe;
result->UnionType.kind = kind;
result->UnionType.where_token = where_token;
result->UnionType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *align = nullptr;
bool no_nil = false;
bool maybe = false;
bool shared_nil = false;
UnionTypeKind union_kind = UnionType_Normal;
Token start_token = f->curr_token;
@@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
no_nil = true;
} else if (tag.string == "shared_nil") {
if (shared_nil) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
shared_nil = true;
} else if (tag.string == "maybe") {
if (maybe) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
@@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (no_nil && maybe) {
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
}
if (no_nil && shared_nil) {
syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together");
}
if (shared_nil && maybe) {
syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together");
}
if (maybe) {
union_kind = UnionType_maybe;
} else if (no_nil) {
union_kind = UnionType_no_nil;
} else if (shared_nil) {
union_kind = UnionType_shared_nil;
}
skip_possible_newline_for_literal(f);
@@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token close = expect_closing_brace_of_field_list(f);
return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
} break;
case Token_enum: {

View File

@@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
"intel",
};
enum UnionTypeKind : u8 {
UnionType_Normal = 0,
UnionType_maybe = 1,
UnionType_no_nil = 2,
UnionType_shared_nil = 3,
};
#define AST_KINDS \
AST_KIND(Ident, "identifier", struct { \
Token token; \
@@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \
Slice<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
bool maybe; \
bool no_nil; \
UnionTypeKind kind; \
Token where_token; \
Slice<Ast *> where_clauses; \
}) \

View File

@@ -165,9 +165,8 @@ struct TypeUnion {
i16 tag_size;
bool is_polymorphic;
bool is_poly_specialized : 1;
bool no_nil : 1;
bool maybe : 1;
bool is_poly_specialized;
UnionTypeKind kind;
};
struct TypeProc {
@@ -186,7 +185,6 @@ struct TypeProc {
bool c_vararg;
bool is_polymorphic;
bool is_poly_specialized;
bool has_proc_default_values;
bool has_named_results;
bool diverging; // no return
bool return_by_pointer;
@@ -694,6 +692,28 @@ gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
OdinAtomicMemoryOrder_consume = 1, // monotonic
OdinAtomicMemoryOrder_acquire = 2,
OdinAtomicMemoryOrder_release = 3,
OdinAtomicMemoryOrder_acq_rel = 4,
OdinAtomicMemoryOrder_seq_cst = 5,
OdinAtomicMemoryOrder_COUNT,
};
char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = {
"Relaxed",
"Consume",
"Acquire",
"Release",
"Acq_Rel",
"Seq_Cst",
};
gb_global Type *t_atomic_memory_order = nullptr;
gb_global RecursiveMutex g_type_mutex;
@@ -1665,7 +1685,7 @@ bool is_type_map(Type *t) {
bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t);
if (t->kind == Type_Union && t->Union.maybe) {
if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_pointer(v) || is_type_multi_pointer(v);
@@ -1677,7 +1697,7 @@ bool is_type_union_maybe_pointer(Type *t) {
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t);
if (t->kind == Type_Union && t->Union.maybe) {
if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
@@ -2169,7 +2189,7 @@ bool type_has_nil(Type *t) {
case Type_Map:
return true;
case Type_Union:
return !t->Union.no_nil;
return t->Union.kind != UnionType_no_nil;
case Type_Struct:
if (is_type_soa_struct(t)) {
switch (t->Struct.soa_kind) {
@@ -2455,7 +2475,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
if (y->kind == Type_Union) {
if (x->Union.variants.count == y->Union.variants.count &&
x->Union.custom_align == y->Union.custom_align &&
x->Union.no_nil == y->Union.no_nil) {
x->Union.kind == y->Union.kind) {
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2599,7 +2619,7 @@ i64 union_variant_index(Type *u, Type *v) {
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
if (u->Union.no_nil) {
if (u->Union.kind == UnionType_no_nil) {
return cast(i64)(i+0);
} else {
return cast(i64)(i+1);
@@ -4022,8 +4042,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
case Type_Union:
str = gb_string_appendc(str, "union");
if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil");
if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe");
switch (type->Union.kind) {
case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break;
case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
}
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
str = gb_string_appendc(str, " {");
for_array(i, type->Union.variants) {

Some files were not shown because too many files have changed in this diff Show More