mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-23 18:29:44 +00:00
Merge branch 'master' into windows-llvm-13.0.0
This commit is contained in:
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -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: |
|
||||
|
||||
79
Makefile
79
Makefile
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
483
core/math/ease/ease.odin
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import sync "core:sync/sync2"
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package os2
|
||||
|
||||
import sync "core:sync/sync2"
|
||||
import "core:sync"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package os
|
||||
|
||||
import "core:time"
|
||||
|
||||
|
||||
File_Info :: struct {
|
||||
fullpath: string,
|
||||
name: string,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build darwin
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build freebsd
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:os"
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build openbsd
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:os"
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//+build darwin
|
||||
//+private
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
46
core/sync/primitives_freebsd.odin
Normal file
46
core/sync/primitives_freebsd.odin
Normal 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)
|
||||
}
|
||||
125
core/sync/primitives_internal.odin
Normal file
125
core/sync/primitives_internal.odin
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
47
core/sync/primitives_linux.odin
Normal file
47
core/sync/primitives_linux.odin
Normal 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)
|
||||
}
|
||||
46
core/sync/primitives_openbsd.odin
Normal file
46
core/sync/primitives_openbsd.odin
Normal 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)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//+build windows
|
||||
//+private
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
import win32 "core:sys/windows"
|
||||
@@ -1,5 +1,5 @@
|
||||
//+private
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build freebsd
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:os"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build linux
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return unix.sys_gettid()
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build openbsd
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:os"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
/*
|
||||
Example:
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
|
||||
66
core/sys/windows/gdi32.odin
Normal file
66
core/sys/windows/gdi32.odin
Normal 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 ---
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
252
core/sys/windows/key_codes.odin
Normal file
252
core/sys/windows/key_codes.odin
Normal 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
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
198
core/sys/windows/user32.odin
Normal file
198
core/sys/windows/user32.odin
Normal 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
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
|
||||
1048
core/sys/windows/window_messages.odin
Normal file
1048
core/sys/windows/window_messages.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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, ¶ms)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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, ¶m_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, ¶m_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);
|
||||
|
||||
@@ -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 != "";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -115,6 +115,16 @@ struct ParameterValue {
|
||||
};
|
||||
};
|
||||
|
||||
bool has_parameter_value(ParameterValue const ¶m_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,
|
||||
};
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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; \
|
||||
}) \
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user