From: Brendan Hansen Date: Tue, 21 Mar 2023 13:58:33 +0000 (-0500) Subject: changed: converted documentation to `#doc` X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=fca0aeaaa8e3679c6281f337236e3a98b1d0fa62;p=onyx.git changed: converted documentation to `#doc` --- diff --git a/core/builtin.onyx b/core/builtin.onyx index f0ab0944..2d354a2b 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -58,16 +58,17 @@ range :: struct { // casts to all other pointer types. null :: cast(rawptr) 0 -// -// `null_proc` is a special function that breaks the normal rules of type -// checking. `null_proc`, or any procedure marked with `#null`, is assignable -// to any function type, regardless of if the types match. For example, -// -// f: (i32) -> i32 = null_proc; -// -// Even though `null_proc` is a `() -> void` function, it bypasses that check -// and gets assigned to `f`. If f is called, there will be a runtime exception. -// This is by design. +#doc """ + `null_proc` is a special function that breaks the normal rules of type + checking. `null_proc`, or any procedure marked with `#null`, is assignable + to any function type, regardless of if the types match. For example, + + f: (i32) -> i32 = null_proc; + + Even though `null_proc` is a `() -> void` function, it bypasses that check + and gets assigned to `f`. If f is called, there will be a runtime exception. + This is by design. +""" null_proc :: () -> void #null --- // @@ -364,28 +365,29 @@ cfree :: (ptr: rawptr) => raw_free(context.allocator, ptr); } -// -// Represents a generic "iterator" or "generator" of a specific -// type. Can be used in a for-loop natively. -// -// `data` is used for contextual information and is passed to -// the `next`, `close`, and `remove` procedures. -// -// `next` is used to extract the next value out of the iterator. -// It returns the next value, and a continuation flag. If the -// flag is false, the value should be ignored and iteration should -// stop. -// -// `close` should called when the iterator has ended. This is -// done automatically in for-loops, and in the `core.iter` library. -// In for-loops, `close` is called no matter which way the for-loop -// exits (`break`, `return`, etc). Using this rule, iterator can -// be used to create "resources" that automatically close when you -// are done with them. -// -// `remove` is used to tell the iterator to remove the last value -// returned from some underlying data store. Invoked automatically -// using the `#remove` directive in a for-loop. +#doc """ + Represents a generic "iterator" or "generator" of a specific + type. Can be used in a for-loop natively. + + `data` is used for contextual information and is passed to + the `next`, `close`, and `remove` procedures. + + `next` is used to extract the next value out of the iterator. + It returns the next value, and a continuation flag. If the + flag is false, the value should be ignored and iteration should + stop. + + `close` should called when the iterator has ended. This is + done automatically in for-loops, and in the `core.iter` library. + In for-loops, `close` is called no matter which way the for-loop + exits (`break`, `return`, etc). Using this rule, iterator can + be used to create "resources" that automatically close when you + are done with them. + + `remove` is used to tell the iterator to remove the last value + returned from some underlying data store. Invoked automatically + using the `#remove` directive in a for-loop. +""" Iterator :: struct (Iter_Type: type_expr) { data: rawptr; next: (data: rawptr) -> (Iter_Type, bool); @@ -394,13 +396,14 @@ Iterator :: struct (Iter_Type: type_expr) { } -// -// Optional represents the possibility of a value being empty, without -// resorting to pointers and null-pointers. Most of the functionality -// for Optional is defined in core/containers/optional.onyx. This -// definition exists here because the compiler use it as the template -// for types like '? i32'. In other words, '? i32' is equivalent to -// 'Optional(i32)'. +#doc """ + Optional represents the possibility of a value being empty, without + resorting to pointers and null-pointers. Most of the functionality + for Optional is defined in core/containers/optional.onyx. This + definition exists here because the compiler use it as the template + for types like '? i32'. In other words, '? i32' is equivalent to + 'Optional(i32)'. +""" Optional :: struct (Value_Type: type_expr) { has_value: bool; value: Value_Type; @@ -408,11 +411,12 @@ Optional :: struct (Value_Type: type_expr) { -// -// This structure represents the result of a '#callsite' expression. Currently, -// #callsite is only valid (and parsed) as a default value for a procedure parameter. -// It allows the function to get the address of the calling site, which can be -// used for error printing, unique hashes, and much more. +#doc """ + This structure represents the result of a '#callsite' expression. Currently, + #callsite is only valid (and parsed) as a default value for a procedure parameter. + It allows the function to get the address of the calling site, which can be + used for error printing, unique hashes, and much more. +""" CallSite :: struct { file : str; line : u32; @@ -420,31 +424,34 @@ CallSite :: struct { } -// -// This structure is used to represent any value in the language. -// It contains a pointer to the data, and the type of the value. -// Using the `core.misc` library, you can easily manipulate `any`s -// and build runtime polymorphism. +#doc """ + This structure is used to represent any value in the language. + It contains a pointer to the data, and the type of the value. + Using the `core.misc` library, you can easily manipulate `any`s + and build runtime polymorphism. +""" any :: struct { data: rawptr; type: type_expr; } -// -// Represents a code block that can be passed around at compile-time. -// This is commonly used with macros or polymorphic procedures to create -// very power extensions to the syntax. +#doc """ + Represents a code block that can be passed around at compile-time. + This is commonly used with macros or polymorphic procedures to create + very power extensions to the syntax. +""" Code :: struct {_:i32;} -// -// Define aliases for common datastructures in the core library, if the core library is available. -// I'm on the fence about keeping this, as the programmer may want to use these names for their own -// structures, but for the moment I don't see any harm. I'm also thinking about removing the '[..]' -// syntax for dynamic arrays and just make them like Map's and Set's, i.e. Array(T). This would -// remove some confusion around the 3 different array types as dynamic arrays would clearly just be -// normal structures. With the recent addition of macros and iterators, there really wouldn't be much -// difference anyway. +#doc """ + Define aliases for common datastructures in the core library, if the core library is available. + I'm on the fence about keeping this, as the programmer may want to use these names for their own + structures, but for the moment I don't see any harm. I'm also thinking about removing the '[..]' + syntax for dynamic arrays and just make them like Map's and Set's, i.e. Array(T). This would + remove some confusion around the 3 different array types as dynamic arrays would clearly just be + normal structures. With the recent addition of macros and iterators, there really wouldn't be much + difference anyway. +""" #if #defined((package core.map).Map) { Map :: (package core.map).Map; } @@ -454,26 +461,30 @@ Code :: struct {_:i32;} } -// -// This procedure is a special compiler generated procedure that initializes all the data segments -// in the program. It should only be called once, by the main thread, at the start of execution. It -// is undefined behaviour if it is called more than once. +#doc """ + This procedure is a special compiler generated procedure that initializes all the data segments + in the program. It should only be called once, by the main thread, at the start of execution. It + is undefined behaviour if it is called more than once. +""" __initialize_data_segments :: () -> void --- -// -// This is also a special compiler generated procedure that calls all procedures specified with -// #init, in the specified order. It should theoretically only be called once on the main thread. +#doc """ + This is a special compiler generated procedure that calls all procedures specified with `#init` + in the specified order. It should theoretically only be called once on the main thread. +""" __run_init_procedures :: () -> void --- -// -// This overloaded procedure allow you to define an implicit rule for how to convert any value -// into a boolean. A default is provided for ALL pointer types and array types, but this can -// be used for structures or distinct types. +#doc """ + This overloaded procedure allow you to define an implicit rule for how to convert any value + into a boolean. A default is provided for ALL pointer types and array types, but this can + be used for structures or distinct types. +""" __implicit_bool_cast :: #match -> bool {} -// -// Defines all options for changing the memory layout, imports and exports, -// and more of an Onyx binary. +#doc """ + Defines all options for changing the memory layout, imports and exports, + and more of an Onyx binary. +""" Link_Options :: struct { // By default the memory layout of a binary is: // reserved | static-data | stack | heap @@ -527,15 +538,16 @@ Link_Options :: struct { } -// -// Special type used to represent a package at runtime. -// For example, -// -// x: package_id = package main -// -// Currently, there is not much you can do with this; it is -// only used by the runtime.info library if you want to filter -// tags based on which package they are coming from. +#doc """ + Special type used to represent a package at runtime. + For example, + + x: package_id = package main + + Currently, there is not much you can do with this; it is + only used by the runtime.info library if you want to filter + tags based on which package they are coming from. +""" package_id :: #distinct u32 // diff --git a/core/conv/conv.onyx b/core/conv/conv.onyx index e3975f64..b65b738a 100644 --- a/core/conv/conv.onyx +++ b/core/conv/conv.onyx @@ -4,10 +4,11 @@ Enable_Custom_Formatters :: true use core {string, math} -// -// Converts a string into an integer. Works with positive and -// negative integers. If given a pointer to a string, will -// modify the string to extract the integer part. +#doc """ + Converts a string into an integer. Works with positive and + negative integers. If given a pointer to a string, will + modify the string to extract the integer part. +""" str_to_i64 :: #match #local {} #overload @@ -66,8 +67,7 @@ str_to_i64 :: (s: &str, base: u32 = 10) -> i64 { } -// -// Converts a string to a floating point number. +#doc "Converts a string to a floating point number." str_to_f64 :: #match #local {} #overload @@ -143,11 +143,12 @@ str_to_f64 :: (s: &str) -> f64 { } -// -// Converts an integer into a string using the buffer provided. -// Supports upto base 64. If prefix is true, binary numbers are -// prefixed with '0b' and hexadecimal numbers are prefixed with -// '0x'. +#doc """ + Converts an integer into a string using the buffer provided. + Supports upto base 64. If prefix is true, binary numbers are + prefixed with '0b' and hexadecimal numbers are prefixed with + '0x'. +""" i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str { is_neg := false; if n < 0 && base == 10 { @@ -213,9 +214,10 @@ i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) } -// -// Converts an unsigned number into a string using the buffer provided. -// Behaves like i64_to_str. +#doc """ + Converts an unsigned number into a string using the buffer provided. + Behaves like i64_to_str. +""" u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str { c: [&] u8 = &buf[buf.count - 1]; len := 0; @@ -268,12 +270,13 @@ u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) return str.{ data = c + 1, count = len }; } -// -// Converts a floating point number into a string, using -// the buffer provided. -// -// This is better than what used to be, but still relies on converting the integer -// part of the float to an integer, which could overflow. +#doc """ + Converts a floating point number into a string, using + the buffer provided. + + This is better than what used to be, but still relies on converting the integer + part of the float to an integer, which could overflow. +""" f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str { if math.is_nan(f) { return format(buf, "NaN"); diff --git a/core/conv/format.onyx b/core/conv/format.onyx index 59a4c196..668b8e67 100644 --- a/core/conv/format.onyx +++ b/core/conv/format.onyx @@ -7,10 +7,11 @@ use core {map, string, array, math} custom_parsers : Map(type_expr, #type (rawptr, str, Allocator) -> bool); } -// -// This procedure is run before main() as it is an #init procedure. -// It looks for all custom formatting and parsing definitions and -// registers them to be used in format_any and parse_any. +#doc """ + This procedure is run before main() as it is an #init procedure. + It looks for all custom formatting and parsing definitions and + registers them to be used in format_any and parse_any. +""" custom_formatters_initialized :: #init () { map.init(&custom_formatters, default=null_proc); map.init(&custom_parsers, default=null_proc); @@ -55,72 +56,76 @@ custom_formatters_initialized :: #init () { } } -// -// Registers a formatting function for a particular type. This type is -// inferred from the type of the third argument in the given function. +#doc """ + Registers a formatting function for a particular type. This type is + inferred from the type of the third argument in the given function. +""" register_custom_formatter :: (formatter: (&Format_Output, &Format, &$T) -> void) { custom_formatters[T] = formatter; } -// -// Registers a parsing function for a particular type. This type is -// inferred from the type of the first argument in the given function. +#doc """ + Registers a parsing function for a particular type. This type is + inferred from the type of the first argument in the given function. +""" register_custom_parser :: (parser: (&$T, str, Allocator) -> bool) { custom_parsers[T] = parser; } -// -// Tag-type used to specify how to format a structure. -// -// @conv.Custom_Format.{ format_structure } -// TheStructure :: struct { ... } -// +#doc """ + Tag-type used to specify how to format a structure. + + @conv.Custom_Format.{ format_structure } + TheStructure :: struct { ... } +""" Custom_Format :: struct { format: (&Format_Output, &Format, rawptr) -> void; } -// -// Tag-type used to specify that a certain procedure should be used -// to format a type. -// -// @conv.Custom_Format_Proc.{ TheStructure } -// format_structure :: (output: &conv.Format_Output, format: &conv.Format, data: &TheStructure) { ... } -// +#doc """ + Tag-type used to specify that a certain procedure should be used + to format a type. + + @conv.Custom_Format_Proc.{ TheStructure } + format_structure :: (output: &conv.Format_Output, format: &conv.Format, data: &TheStructure) { ... } +""" Custom_Format_Proc :: struct { type: type_expr; } -// -// Tag-type used to specify how to parse a structure. -// -// @conv.Custom_Parse.{ parse_structure } -// TheStructure :: struct { ... } -// +#doc """ + Tag-type used to specify how to parse a structure. + + @conv.Custom_Parse.{ parse_structure } + TheStructure :: struct { ... } +""" Custom_Parse :: struct { parse: (rawptr, str, Allocator) -> bool; } -// -// Tag-type used to specify that a certain procedure should be used -// to parse a type. -// -// @conv.Custom_Parse_Proc.{ TheStructure } -// parse_structure :: (data: &TheStructure, input: str, allocator: Allocator) -> bool { ... } -// +#doc """ + Tag-type used to specify that a certain procedure should be used + to parse a type. + + @conv.Custom_Parse_Proc.{ TheStructure } + parse_structure :: (data: &TheStructure, input: str, allocator: Allocator) -> bool { ... } + +""" Custom_Parse_Proc :: struct { type: type_expr; } -// -// Passed to any custom formatter. Wraps outputting data to any source, -// using a `flush` callback function. Use `write` to output a string. -// When the internal buffer is filled, `flush` is called to empty the -// buffer to the final destination. +#doc """ + Passed to any custom formatter. Wraps outputting data to any source, + using a `flush` callback function. Use `write` to output a string. + When the internal buffer is filled, `flush` is called to empty the + buffer to the final destination. +""" Format_Output :: struct { data: [&] u8; count: u32; @@ -164,8 +169,7 @@ Format_Flush_Callback :: struct { } -// -// Formatting options passed to a custom formatter. +#doc "Formatting options passed to a custom formatter." Format :: struct { pretty_printing := false; // p quote_strings := false; // " @@ -194,9 +198,10 @@ str_format :: format str_format_va :: format_va -// -// Formats a string using the provided arguments and format specified string. -// This has many overloads to make it easy to work with. +#doc """ + Formats a string using the provided arguments and format specified string. + This has many overloads to make it easy to work with. +""" format :: #match {} #overload @@ -228,8 +233,9 @@ format :: (buffer: &dyn_str, format: str, va: ..any) -> str { return *buffer; } -// -// Like format(), but takes the arguments as an array of `any`s, not a variadic argument array. +#doc """ + Like `format`, but takes the arguments as an array of `any`s, not a variadic argument array. +""" format_va :: #match {} #overload @@ -396,10 +402,11 @@ format_va :: (output: &Format_Output, format: str, va: [] any) -> str { } -// -// This procedure converts any value into a string, using the type information system. -// If a custom formatter is specified for the type, that is used instead. -// This procedure is generally not used directly; instead, through format or format_va. +#doc """ + This procedure converts any value into a string, using the type information system. + If a custom formatter is specified for the type, that is used instead. + This procedure is generally not used directly; instead, through format or format_va. +""" format_any :: (output: &Format_Output, formatting: &Format, v: any) { use package runtime.info array :: package core.array; diff --git a/core/conv/parse.onyx b/core/conv/parse.onyx index d0f7fe1e..9156de0d 100644 --- a/core/conv/parse.onyx +++ b/core/conv/parse.onyx @@ -2,9 +2,10 @@ package core.conv use core {map, string, array, math} -// -// Parses many different types from a string into a value. -// Uses a custom parser if one has been specified for the type given. +#doc """ + Parses many different types from a string into a value. + Uses a custom parser if one has been specified for the type given. +""" parse_any :: #match {} #overload diff --git a/core/misc/any_utils.onyx b/core/misc/any_utils.onyx index 31d02351..c6c71cd2 100644 --- a/core/misc/any_utils.onyx +++ b/core/misc/any_utils.onyx @@ -24,8 +24,7 @@ any_as :: (a: any, $T: type_expr) -> &T { return cast(&T) a.data; } -// -// Dereference a pointer any. +#doc "Dereference a pointer any." any_dereference :: (v: any) -> any { t := get_type_info(v.type); if t.kind == .Pointer { @@ -36,8 +35,8 @@ any_dereference :: (v: any) -> any { return v; } -// -// Subscript an array-like any. + +#doc "Subscript an array-like any." any_subscript :: (v: any, index: i32) -> any { base_ptr, elem_type, count := any_as_array(v); if index >= count || index < 0 { @@ -50,8 +49,7 @@ any_subscript :: (v: any, index: i32) -> any { }; } -// -// Select a member from an any. +#doc "Select a member from an any." any_selector :: (v: any, member_name: str) -> any { t := get_type_info(v.type); if t.kind == .Struct { @@ -64,8 +62,7 @@ any_selector :: (v: any, member_name: str) -> any { return .{null, void}; } -// -// This selector works with selecting "foo.bar.joe" +#doc "Like `any_selector`, but works with selecting \"foo.bar.joe\"." any_nested_selector :: (v: any, member_name: str) -> any { t := get_type_info(v.type); if t.kind != .Struct do return .{}; @@ -86,20 +83,20 @@ any_nested_selector :: (v: any, member_name: str) -> any { return .{null, void}; } -// -// Convert a structure or pointer to a structure to a Map with -// keys representing the fields of the structure, and values -// representing the value of each field. -// -// T :: struct { -// x := 123; -// y := "test"; -// } -// -// m := any_to_map(T.{}); -// -// `m` would have two keys, "x" and "y". -// +#doc """ + Convert a structure or pointer to a structure to a Map with + keys representing the fields of the structure, and values + representing the value of each field. + + T :: struct { + x := 123; + y := "test"; + } + + m := any_to_map(T.{}); + + `m` would have two keys, "x" and "y". +""" any_to_map :: (v: any) -> (Map(str, any), success: bool) { vals := v; if get_type_info(vals.type).kind == .Pointer { @@ -119,8 +116,7 @@ any_to_map :: (v: any) -> (Map(str, any), success: bool) { return out, true; } -// -// Creates an iterator out of an array-like any. +#doc "Creates an iterator out of an array-like any." any_iter :: (arr: any) -> Iterator(any) { base_ptr, elem_type, count := any_as_array(arr); if count == 0 { diff --git a/core/sync/barrier.onyx b/core/sync/barrier.onyx index eae7d7e3..d64115fc 100644 --- a/core/sync/barrier.onyx +++ b/core/sync/barrier.onyx @@ -13,9 +13,10 @@ package core.sync // continue processing. // -// -// Represents a generational barrier, so the same barrier -// can be used safely multiple times. +#doc """ + Represents a generational barrier, so the same barrier + can be used safely multiple times. +""" Barrier :: struct { mutex : Mutex; cond : Condition_Variable; @@ -25,8 +26,8 @@ Barrier :: struct { thread_count : i32; } -// -// Initializes a new generational barrier with `thread_count` threads. + +#doc "Initializes a new generational barrier with `thread_count` threads." barrier_init :: (b: &Barrier, thread_count: i32) { mutex_init(&b.mutex); condition_init(&b.cond); @@ -36,16 +37,17 @@ barrier_init :: (b: &Barrier, thread_count: i32) { b.thread_count = thread_count; } -// -// Destroys a generational barrier. + +#doc "Destroys a generational barrier." barrier_destroy :: (b: &Barrier) { mutex_destroy(&b.mutex); condition_destroy(&b.cond); } -// -// Signals that a thread has reached the barrier. -// The last thread to reach the barrier will wake up all other threads. +#doc """ + Signals that a thread has reached the barrier. + The last thread to reach the barrier will wake up all other threads. +""" barrier_wait :: (b: &Barrier) { mutex_lock(&b.mutex); defer mutex_unlock(&b.mutex); diff --git a/core/sync/condition_variable.onyx b/core/sync/condition_variable.onyx index 82447f6e..d53f00c8 100644 --- a/core/sync/condition_variable.onyx +++ b/core/sync/condition_variable.onyx @@ -2,26 +2,22 @@ package core.sync // TODO: Free the semaphores after they are used. -// -// A condition variable is used to implement a queue of threads -// waiting for a condition to be true. Each thread joins the queue -// using `condition_wait`. Then, another thread can signal that -// the condition has changed and can "wake up" the first thread in -// the queue using `condition_signal`. Alternatively, all threads -// can be woken up using `condition_broadcast`. -// -// Condition variables are generally used to prevent spin checking -// a condition and waiting for it to change. Instead, the thread -// joins a wait-queue, and leave it up to another thread to wake -// it up to continue processing. However sadly, in WebAssembly this -// is not possible because with the atomic_wait and atomic_notify -// instructions, which currently are not supported by any runtime -// outside of the browser. -// - -// -// Represents a condition variable, with a mutex used to -// protect the queue operations. +#doc """ + A condition variable is used to implement a queue of threads + waiting for a condition to be true. Each thread joins the queue + using `condition_wait`. Then, another thread can signal that + the condition has changed and can "wake up" the first thread in + the queue using `condition_signal`. Alternatively, all threads + can be woken up using `condition_broadcast`. + + Condition variables are generally used to prevent spin checking + a condition and waiting for it to change. Instead, the thread + joins a wait-queue, and leave it up to another thread to wake + it up to continue processing. However sadly, in WebAssembly this + is not possible because with the atomic_wait and atomic_notify + instructions, which currently are not supported by any runtime + outside of the browser. +""" Condition_Variable :: struct { Node :: struct { semaphore : Semaphore; @@ -32,25 +28,24 @@ Condition_Variable :: struct { queue: &Node; } -// -// Initializes a new condition variable. +#doc "Initializes a new condition variable." condition_init :: (c: &Condition_Variable) { mutex_init(&c.mutex); c.queue = null; } -// -// Destroys a condition variable. +#doc "Destroys a condition variable." condition_destroy :: (c: &Condition_Variable) { if c.queue != null do condition_broadcast(c); mutex_destroy(&c.mutex); } -// -// Enters the thread in the wait-queue of the condition variable. -// If `m` is not null, the mutex will first be released before -// entering the queue, and then relocked before returning. +#doc """ + Enters the thread in the wait-queue of the condition variable. + If `m` is not null, the mutex will first be released before + entering the queue, and then relocked before returning. +""" condition_wait :: (c: &Condition_Variable, m: &Mutex) { node: Condition_Variable.Node; @@ -65,8 +60,8 @@ condition_wait :: (c: &Condition_Variable, m: &Mutex) { if m != null do mutex_lock(m); } -// -// Wakes up one thread from the wait-queue. + +#doc "Wakes up one thread from the wait-queue." condition_signal :: (c: &Condition_Variable) { scoped_mutex(&c.mutex); @@ -76,8 +71,8 @@ condition_signal :: (c: &Condition_Variable) { } } -// -// Wakes up all threads from the wait-queue. + +#doc "Wakes up all threads from the wait-queue." condition_broadcast :: (c: &Condition_Variable) { scoped_mutex(&c.mutex); diff --git a/core/sync/mutex.onyx b/core/sync/mutex.onyx index fd908997..1705612d 100644 --- a/core/sync/mutex.onyx +++ b/core/sync/mutex.onyx @@ -3,51 +3,51 @@ package core.sync use core.intrinsics.atomics use core.thread { Thread_ID } -// -// A mutex represents a resource that can only be held by one -// thread at a time. It is used to create sections of code that -// only one thread can be in at a time. -// -// Mutexes in WebAssembly are very cheap, because they simply -// use the atomic_cmpxchg intrinsic to operate. This only uses -// memory, so no real resource allocation is necessary. -// -// `lock` has two states: 0, and 1. -// 0 means unlocked -// 1 means locked -// -// To lock it: -// Try to store 1 if the value was already 0 -// Otherwise, if it was already 1, wait until it goes to 0. -// -// To unlock it: -// Atomically set it to 0. -// Notify at most 1 other thread about this change. -// +#doc """ + A mutex represents a resource that can only be held by one + thread at a time. It is used to create sections of code that + only one thread can be in at a time. + + Mutexes in WebAssembly are very cheap, because they simply + use the atomic_cmpxchg intrinsic to operate. This only uses + memory, so no real resource allocation is necessary. + + `lock` has two states: 0, and 1. + 0 means unlocked, 1 means locked + + To lock it: + Try to store 1 if the value was already 0. + Otherwise, if it was already 1, wait until it goes to 0. + + To unlock it: + Atomically set it to 0. + Notify at most 1 other thread about this change. +""" Mutex :: struct { lock : i32; owner : Thread_ID; } -// -// Initializes a new mutex. + +#doc "Initializes a new mutex." mutex_init :: (m: &Mutex) { m.lock = 0; m.owner = -1; } -// -// Destroys a mutex. + +#doc "Destroys a mutex." mutex_destroy :: (m: &Mutex) { m.lock = -1; m.owner = -1; } -// -// Locks a mutex. If the mutex is currently held by another thread, -// this function enters a spin loop until the mutex is unlocked. -// In a JavaScript based implementation, the __atomic_wait intrinsic -// is used to avoid having to spin loop. +#doc """ + Locks a mutex. If the mutex is currently held by another thread, + this function enters a spin loop until the mutex is unlocked. + In a JavaScript based implementation, the __atomic_wait intrinsic + is used to avoid having to spin loop. +""" mutex_lock :: (m: &Mutex) { while __atomic_cmpxchg(&m.lock, 0, 1) == 1 { if m.owner == context.thread_id do return; @@ -68,10 +68,11 @@ mutex_lock :: (m: &Mutex) { m.owner = context.thread_id; } -// -// Unlocks a mutex, if the calling thread currently holds the mutex. -// In a JavaScript based implementation, the __atomic_notify intrinsic -// is used to wake up one waiting thread. +#doc """ + Unlocks a mutex, if the calling thread currently holds the mutex. + In a JavaScript based implementation, the __atomic_notify intrinsic + is used to wake up one waiting thread. +""" mutex_unlock :: (m: &Mutex) { if m.owner != context.thread_id do return; @@ -83,17 +84,17 @@ mutex_unlock :: (m: &Mutex) { } } -// -// Helpful macro for making a particular block be protected by a macro. -// -// m: sync.Mutx; -// sync.mutex_init(&m); -// -// { -// sync.scoped_mutex(&m); -// // Everything here is done by one thread at a time. -// } -// +#doc """ + Helpful macro for making a particular block be protected by a macro. + + m: sync.Mutx; + sync.mutex_init(&m); + + { + sync.scoped_mutex(&m); + // Everything here is done by one thread at a time. + } +""" scoped_mutex :: macro (m: &Mutex) { ml :: mutex_lock mu :: mutex_unlock @@ -102,17 +103,17 @@ scoped_mutex :: macro (m: &Mutex) { defer mu(m); } -// -// Abstracts the pattern decribed in scoped_mutex by automatically -// calling scoped_mutex in the block of code given. -// -// m: sync.Mutx; -// sync.mutex_init(&m); -// -// sync.critical_section(&m) { -// // Everything here is done by one thread at a time. -// } -// +#doc """ + Abstracts the pattern decribed in scoped_mutex by automatically + calling scoped_mutex in the block of code given. + + m: sync.Mutx; + sync.mutex_init(&m); + + sync.critical_section(&m) { + // Everything here is done by one thread at a time. + } +""" critical_section :: macro (m: &Mutex, body: Code) -> i32 { scoped_mutex :: scoped_mutex; scoped_mutex(m); diff --git a/core/sync/once.onyx b/core/sync/once.onyx index 8c182bda..1e711951 100644 --- a/core/sync/once.onyx +++ b/core/sync/once.onyx @@ -5,8 +5,7 @@ package core.sync // function only once. It is simply a flag with a mutex. // -// -// Represents something will only happen once. +#doc "Represents something will only happen once." Once :: struct { done: bool; mutex: Mutex; @@ -14,8 +13,7 @@ Once :: struct { #inject Once.exec :: #match #local {} -// -// Run a function with no arguments once. +#doc "Run a function with no arguments once." #overload Once.exec :: (o: &Once, f: () -> $R) { scoped_mutex(&o.mutex); @@ -25,8 +23,7 @@ Once.exec :: (o: &Once, f: () -> $R) { f(); } -// -// Run a function with one argument once. +#doc "Run a function with one argument once." #overload Once.exec :: (o: &Once, ctx: $Ctx, f: (Ctx) -> $R) { scoped_mutex(&o.mutex); diff --git a/core/sync/semaphore.onyx b/core/sync/semaphore.onyx index d7df97a1..3314e7b8 100644 --- a/core/sync/semaphore.onyx +++ b/core/sync/semaphore.onyx @@ -2,44 +2,43 @@ package core.sync use core.intrinsics.atomics -// -// A semaphore represents a counter that can only be incremented -// and decremented by one thread at a time. "Waiting" on a semaphore -// means decrementing the counter by 1 if it is greater than 0, otherwise -// waiting until the counter is incremented. "Posting" on a semaphore -// means incrementing the counter by a certain value, in turn releasing -// other threads that might have been waiting for the value to change. -// -// Semaphores are generally used for controlling access to shared -// resources. For a contrived example, say only 4 threads can use -// a given network connection at a time. A semaphore would be created -// with a value of 4. When a thread wants to use the network connection, -// it would use `semaphore_wait` to obtain the resource, or wait if -// the network is currently available. When it is done using the -// network, it would call `semaphore_post` to release the resource, -// allowing another thread to use it. -// +#doc """ + A semaphore represents a counter that can only be incremented + and decremented by one thread at a time. "Waiting" on a semaphore + means decrementing the counter by 1 if it is greater than 0, otherwise + waiting until the counter is incremented. "Posting" on a semaphore + means incrementing the counter by a certain value, in turn releasing + other threads that might have been waiting for the value to change. + + Semaphores are generally used for controlling access to shared + resources. For a contrived example, say only 4 threads can use + a given network connection at a time. A semaphore would be created + with a value of 4. When a thread wants to use the network connection, + it would use `semaphore_wait` to obtain the resource, or wait if + the network is currently available. When it is done using the + network, it would call `semaphore_post` to release the resource, + allowing another thread to use it. +""" Semaphore :: struct { mutex : Mutex; counter : i32; } -// -// Initializes a semaphore with the specified value. +#doc "Initializes a semaphore with the specified value." semaphore_init :: (s: &Semaphore, value: i32) { s.counter = value; mutex_init(&s.mutex); } -// -// Destroys a semaphore. + +#doc "Destroys a semaphore." semaphore_destroy :: (s: &Semaphore) { mutex_destroy(&s.mutex); } -// -// Increment the counter in a semaphore by `count`. + +#doc "Increment the counter in a semaphore by `count`." semaphore_post :: (s: &Semaphore, count := 1) { if count == 0 do return; @@ -54,8 +53,7 @@ semaphore_post :: (s: &Semaphore, count := 1) { } } -// -// Waits until the thread is able to decrement one from the semaphore. +#doc "Waits until the thread is able to decrement one from the semaphore." semaphore_wait :: (s: &Semaphore) { while true { mutex_lock(&s.mutex); diff --git a/core/threads/thread.onyx b/core/threads/thread.onyx index 327ac513..cf0b69cb 100644 --- a/core/threads/thread.onyx +++ b/core/threads/thread.onyx @@ -9,24 +9,26 @@ use core.intrinsics.atomics thread_map : Map(Thread_ID, &Thread); } -// -// An id of a thread. + +#doc "An id of a thread." Thread_ID :: #type i32 -// -// Represents a thread. Currently, this is very simple; just the id -// of the thread and whether or not it is alive. +#doc """ + Represents a thread. Currently, this is very simple; just the id + of the thread and whether or not it is alive. +""" Thread :: struct { id : Thread_ID; alive : bool; } -// -// Spawns a new thread using the runtime.__spawn_thread function. -// The primary job of this function is to create the thread-local -// storage and stack for the new thread, and pass those on. -// Currently the stack size is not controllable, but that could -// be remedied. +#doc """ + Spawns a new thread using the runtime.__spawn_thread function. + The primary job of this function is to create the thread-local + storage and stack for the new thread, and pass those on. + Currently the stack size is not controllable, but that could + be remedied. +""" spawn :: (t: &Thread, data: &$T, func: (&T) -> void) { sync.scoped_mutex(&thread_mutex); @@ -44,10 +46,11 @@ spawn :: (t: &Thread, data: &$T, func: (&T) -> void) { runtime.platform.__spawn_thread(t.id, tls_base, stack_base, func, data); } -// -// Waits for a thread to finish before returning. -// If the thread was not alive in the first place, -// immediately return. +#doc """ + Waits for a thread to finish before returning. + If the thread was not alive in the first place, + immediately return. +""" join :: (t: &Thread) { while t.alive { #if runtime.Wait_Notify_Available { @@ -59,9 +62,10 @@ join :: (t: &Thread) { } } -// -// Forcefully kill a thread using runtime.__kill_thread. -// Does nothing if the thread was not alive. +#doc """ + Forcefully kill a thread using runtime.__kill_thread. + Does nothing if the thread was not alive. +""" kill :: (t: &Thread) -> i32 { if !t.alive do return -1; @@ -71,16 +75,18 @@ kill :: (t: &Thread) -> i32 { return 1; } -// -// Special procedure that should only be called once globally -// that initialize the map of thread ids to thread data. +#doc """ + Special procedure that should only be called once globally + that initialize the map of thread ids to thread data. +""" __initialize :: () { thread_map->init(); } -// -// Special procedure that is called when a thread exits, -// or by kill() above. +#doc """ + Special procedure that is called when a thread exits, + or by kill() above. +""" __exited :: (id: i32) { sync.scoped_mutex(&thread_mutex);