├── deps ├── zig-clap │ ├── .gitattributes │ ├── .gitignore │ ├── .github │ │ ├── FUNDING.yml │ │ ├── dependabot.yml │ │ └── workflows │ │ │ └── main.yml │ ├── zig.mod │ ├── gyro.zzz │ ├── example │ │ ├── usage.zig │ │ ├── help.zig │ │ ├── simple.zig │ │ ├── simple-ex.zig │ │ ├── streaming-clap.zig │ │ └── README.md.template │ ├── LICENSE │ ├── clap │ │ ├── args.zig │ │ └── parsers.zig │ └── build.zig ├── ztracy │ ├── libs │ │ └── tracy │ │ │ ├── client │ │ │ ├── TracyDxt1.hpp │ │ │ ├── TracyDebug.hpp │ │ │ ├── TracySysTime.hpp │ │ │ ├── TracySysTrace.hpp │ │ │ ├── TracyCallstack.h │ │ │ ├── TracyAlloc.cpp │ │ │ ├── TracyStringHelpers.hpp │ │ │ ├── TracyThread.hpp │ │ │ ├── TracySysTime.cpp │ │ │ ├── TracyFastVector.hpp │ │ │ ├── TracyCallstack.hpp │ │ │ ├── TracyRingBuffer.hpp │ │ │ ├── tracy_SPSCQueue.h │ │ │ └── TracyScoped.hpp │ │ │ ├── common │ │ │ ├── TracyUwp.hpp │ │ │ ├── TracyMutex.hpp │ │ │ ├── TracyApi.h │ │ │ ├── TracyStackFrames.hpp │ │ │ ├── TracyAlign.hpp │ │ │ ├── TracyYield.hpp │ │ │ ├── TracyForceInline.hpp │ │ │ ├── TracySystem.hpp │ │ │ ├── TracyAlloc.hpp │ │ │ ├── TracyProtocol.hpp │ │ │ ├── TracySocket.hpp │ │ │ ├── TracyStackFrames.cpp │ │ │ └── TracySystem.cpp │ │ │ ├── libbacktrace │ │ │ ├── config.h │ │ │ ├── LICENSE │ │ │ ├── filenames.hpp │ │ │ ├── state.cpp │ │ │ ├── posix.cpp │ │ │ ├── mmapio.cpp │ │ │ ├── sort.cpp │ │ │ ├── alloc.cpp │ │ │ └── backtrace.hpp │ │ │ ├── AUTHORS │ │ │ ├── TracyClient.cpp │ │ │ └── LICENSE │ ├── README.md │ └── build.zig └── README.md ├── .gitignore ├── tests ├── timer.zig ├── pipe.zig ├── barrier.zig ├── task.zig ├── cancel-fuzz.zig ├── futex.zig ├── signal.zig ├── select.zig ├── tcp.zig ├── channel.zig └── cancel.zig ├── LICENSE ├── src ├── config.zig ├── sync │ ├── barrier.zig │ └── array_queue.zig ├── platform │ ├── posix_sockets.zig │ ├── cancel_queue.zig │ └── io_sim.zig ├── pipe.zig ├── clock.zig ├── platform.zig ├── list.zig ├── event.zig ├── network.zig └── runtime.zig ├── demos └── one.zig └── README.md /deps/zig-clap/.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text eol=lf 2 | -------------------------------------------------------------------------------- /deps/zig-clap/.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache 2 | zig-out 3 | -------------------------------------------------------------------------------- /deps/zig-clap/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [Hejsil] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-out 2 | zig-cache 3 | 4 | .direnv 5 | .envrc 6 | flake.nix 7 | flake.lock 8 | -------------------------------------------------------------------------------- /deps/zig-clap/zig.mod: -------------------------------------------------------------------------------- 1 | id: aoe2l16htluewam6bfwvv0khsbbno8g8jd7suonifg74u7kd 2 | name: clap 3 | main: clap.zig 4 | license: MIT 5 | dependencies: 6 | -------------------------------------------------------------------------------- /deps/zig-clap/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "11:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyDxt1.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYDXT1_HPP__ 2 | #define __TRACYDXT1_HPP__ 3 | 4 | namespace tracy 5 | { 6 | 7 | void CompressImageDxt1( const char* src, char* dst, int w, int h ); 8 | 9 | } 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyDebug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYPRINT_HPP__ 2 | #define __TRACYPRINT_HPP__ 3 | 4 | #ifdef TRACY_VERBOSE 5 | # include 6 | # define TracyDebug(...) fprintf( stderr, __VA_ARGS__ ); 7 | #else 8 | # define TracyDebug(...) 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /deps/README.md: -------------------------------------------------------------------------------- 1 | This directory contains vendored libraries. 2 | 3 | - ztracy 4 | - url: https://github.com/michal-z/zig-gamedev/tree/main/libs/ztracy 5 | - license: MIT 6 | - rev: c7e96b3 7 | 8 | - zig-clap 9 | - url: https://github.com/Hejsil/zig-clap.git 10 | - license: MIT 11 | - rev: 996821a 12 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyUwp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYUWP_HPP__ 2 | #define __TRACYUWP_HPP__ 3 | 4 | #ifdef _WIN32 5 | # include 6 | # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 7 | # define TRACY_UWP 8 | # endif 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /deps/zig-clap/gyro.zzz: -------------------------------------------------------------------------------- 1 | pkgs: 2 | clap: 3 | version: 0.5.0 4 | license: MIT 5 | description: Simple command line argument parsing library 6 | source_url: "https://github.com/Hejsil/zig-clap" 7 | root: clap.zig 8 | files: 9 | README.md 10 | LICENSE 11 | build.zig 12 | clap/*.zig 13 | example/*.zig 14 | 15 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyMutex.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYMUTEX_HPP__ 2 | #define __TRACYMUTEX_HPP__ 3 | 4 | #if defined _MSC_VER 5 | 6 | # include 7 | 8 | namespace tracy 9 | { 10 | using TracyMutex = std::shared_mutex; 11 | } 12 | 13 | #else 14 | 15 | #include 16 | 17 | namespace tracy 18 | { 19 | using TracyMutex = std::mutex; 20 | } 21 | 22 | #endif 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyApi.h: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYAPI_H__ 2 | #define __TRACYAPI_H__ 3 | 4 | #if defined _WIN32 5 | # if defined TRACY_EXPORTS 6 | # define TRACY_API __declspec(dllexport) 7 | # elif defined TRACY_IMPORTS 8 | # define TRACY_API __declspec(dllimport) 9 | # else 10 | # define TRACY_API 11 | # endif 12 | #else 13 | # define TRACY_API __attribute__((visibility("default"))) 14 | #endif 15 | 16 | #endif // __TRACYAPI_H__ 17 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyStackFrames.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSTACKFRAMES_HPP__ 2 | #define __TRACYSTACKFRAMES_HPP__ 3 | 4 | #include 5 | 6 | namespace tracy 7 | { 8 | 9 | struct StringMatch 10 | { 11 | const char* str; 12 | size_t len; 13 | }; 14 | 15 | extern const char** s_tracyStackFrames; 16 | extern const StringMatch* s_tracySkipSubframes; 17 | 18 | static constexpr int s_tracySkipSubframesMinLen = 7; 19 | 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyAlign.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYALIGN_HPP__ 2 | #define __TRACYALIGN_HPP__ 3 | 4 | #include 5 | 6 | #include "TracyForceInline.hpp" 7 | 8 | namespace tracy 9 | { 10 | 11 | template 12 | tracy_force_inline T MemRead( const void* ptr ) 13 | { 14 | T val; 15 | memcpy( &val, ptr, sizeof( T ) ); 16 | return val; 17 | } 18 | 19 | template 20 | tracy_force_inline void MemWrite( void* ptr, T val ) 21 | { 22 | memcpy( ptr, &val, sizeof( T ) ); 23 | } 24 | 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyYield.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYYIELD_HPP__ 2 | #define __TRACYYIELD_HPP__ 3 | 4 | #if defined __SSE2__ || defined _M_AMD64 || _M_IX86_FP == 2 5 | # include 6 | #else 7 | # include 8 | #endif 9 | 10 | #include "TracyForceInline.hpp" 11 | 12 | namespace tracy 13 | { 14 | 15 | static tracy_force_inline void YieldThread() 16 | { 17 | #if defined __SSE2__ || defined _M_AMD64 || _M_IX86_FP == 2 18 | _mm_pause(); 19 | #else 20 | std::this_thread::yield(); 21 | #endif 22 | } 23 | 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/config.h: -------------------------------------------------------------------------------- 1 | #include 2 | #if __WORDSIZE == 64 3 | # define BACKTRACE_ELF_SIZE 64 4 | #else 5 | # define BACKTRACE_ELF_SIZE 32 6 | #endif 7 | 8 | #define HAVE_DLFCN_H 1 9 | #define HAVE_FCNTL 1 10 | #define HAVE_INTTYPES_H 1 11 | #define HAVE_LSTAT 1 12 | #define HAVE_READLINK 1 13 | #define HAVE_DL_ITERATE_PHDR 1 14 | #define HAVE_ATOMIC_FUNCTIONS 1 15 | #define HAVE_DECL_STRNLEN 1 16 | 17 | #ifdef __APPLE__ 18 | # define HAVE_MACH_O_DYLD_H 1 19 | #elif defined BSD 20 | # define HAVE_KERN_PROC 1 21 | # define HAVE_KERN_PROC_ARGS 1 22 | #endif 23 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyForceInline.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYFORCEINLINE_HPP__ 2 | #define __TRACYFORCEINLINE_HPP__ 3 | 4 | #if defined(__GNUC__) 5 | # define tracy_force_inline __attribute__((always_inline)) inline 6 | #elif defined(_MSC_VER) 7 | # define tracy_force_inline __forceinline 8 | #else 9 | # define tracy_force_inline inline 10 | #endif 11 | 12 | #if defined(__GNUC__) 13 | # define tracy_no_inline __attribute__((noinline)) 14 | #elif defined(_MSC_VER) 15 | # define tracy_no_inline __declspec(noinline) 16 | #else 17 | # define tracy_no_inline 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracySysTime.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSYSTIME_HPP__ 2 | #define __TRACYSYSTIME_HPP__ 3 | 4 | #if defined _WIN32 || defined __linux__ || defined __APPLE__ 5 | # define TRACY_HAS_SYSTIME 6 | #else 7 | # include 8 | #endif 9 | 10 | #ifdef BSD 11 | # define TRACY_HAS_SYSTIME 12 | #endif 13 | 14 | #ifdef TRACY_HAS_SYSTIME 15 | 16 | #include 17 | 18 | namespace tracy 19 | { 20 | 21 | class SysTime 22 | { 23 | public: 24 | SysTime(); 25 | float Get(); 26 | 27 | void ReadTimes(); 28 | 29 | private: 30 | uint64_t idle, used; 31 | }; 32 | 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracySystem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSYSTEM_HPP__ 2 | #define __TRACYSYSTEM_HPP__ 3 | 4 | #include 5 | 6 | #include "TracyApi.h" 7 | 8 | namespace tracy 9 | { 10 | 11 | namespace detail 12 | { 13 | TRACY_API uint32_t GetThreadHandleImpl(); 14 | } 15 | 16 | #ifdef TRACY_ENABLE 17 | TRACY_API uint32_t GetThreadHandle(); 18 | #else 19 | static inline uint32_t GetThreadHandle() 20 | { 21 | return detail::GetThreadHandleImpl(); 22 | } 23 | #endif 24 | 25 | TRACY_API void SetThreadName( const char* name ); 26 | TRACY_API const char* GetThreadName( uint32_t id ); 27 | 28 | TRACY_API const char* GetEnvVar(const char* name); 29 | 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracySysTrace.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSYSTRACE_HPP__ 2 | #define __TRACYSYSTRACE_HPP__ 3 | 4 | #if !defined TRACY_NO_SYSTEM_TRACING && ( defined _WIN32 || defined __linux__ ) 5 | # include "../common/TracyUwp.hpp" 6 | # ifndef TRACY_UWP 7 | # define TRACY_HAS_SYSTEM_TRACING 8 | # endif 9 | #endif 10 | 11 | #ifdef TRACY_HAS_SYSTEM_TRACING 12 | 13 | #include 14 | 15 | namespace tracy 16 | { 17 | 18 | bool SysTraceStart( int64_t& samplingPeriod ); 19 | void SysTraceStop(); 20 | void SysTraceWorker( void* ptr ); 21 | 22 | void SysTraceGetExternalName( uint64_t thread, const char*& threadName, const char*& name ); 23 | 24 | } 25 | 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /deps/zig-clap/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, windows-latest] 13 | runs-on: ${{matrix.os}} 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | submodules: recursive 18 | - uses: goto-bus-stop/setup-zig@v1.3.0 19 | with: 20 | version: master 21 | - run: zig build 22 | lint: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: goto-bus-stop/setup-zig@v1.3.0 27 | with: 28 | version: master 29 | - run: zig fmt --check . 30 | -------------------------------------------------------------------------------- /deps/zig-clap/example/usage.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("clap"); 2 | const std = @import("std"); 3 | 4 | pub fn main() !void { 5 | const params = comptime clap.parseParamsComptime( 6 | \\-h, --help Display this help and exit. 7 | \\-v, --version Output version information and exit. 8 | \\ --value An option parameter, which takes a value. 9 | \\ 10 | ); 11 | 12 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); 13 | defer res.deinit(); 14 | 15 | // `clap.usage` is a function that can print a simple help message. It can print any `Param` 16 | // where `Id` has a `value` method (`Param(Help)` is one such parameter). 17 | if (res.args.help) 18 | return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); 19 | } 20 | -------------------------------------------------------------------------------- /deps/zig-clap/example/help.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("clap"); 2 | const std = @import("std"); 3 | 4 | pub fn main() !void { 5 | const params = comptime clap.parseParamsComptime( 6 | \\-h, --help Display this help and exit. 7 | \\-v, --version Output version information and exit. 8 | \\ 9 | ); 10 | 11 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); 12 | defer res.deinit(); 13 | 14 | // `clap.help` is a function that can print a simple help message. It can print any `Param` 15 | // where `Id` has a `describtion` and `value` method (`Param(Help)` is one such parameter). 16 | // The last argument contains options as to how `help` should print those parameters. Using 17 | // `.{}` means the default options. 18 | if (res.args.help) 19 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); 20 | } 21 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyCallstack.h: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYCALLSTACK_H__ 2 | #define __TRACYCALLSTACK_H__ 3 | 4 | #ifndef TRACY_NO_CALLSTACK 5 | 6 | # if !defined _WIN32 7 | # include 8 | # endif 9 | 10 | # if defined _WIN32 11 | # include "../common/TracyUwp.hpp" 12 | # ifndef TRACY_UWP 13 | # define TRACY_HAS_CALLSTACK 1 14 | # endif 15 | # elif defined __ANDROID__ 16 | # if !defined __arm__ || __ANDROID_API__ >= 21 17 | # define TRACY_HAS_CALLSTACK 2 18 | # else 19 | # define TRACY_HAS_CALLSTACK 5 20 | # endif 21 | # elif defined __linux 22 | # if defined _GNU_SOURCE && defined __GLIBC__ 23 | # define TRACY_HAS_CALLSTACK 3 24 | # else 25 | # define TRACY_HAS_CALLSTACK 2 26 | # endif 27 | # elif defined __APPLE__ 28 | # define TRACY_HAS_CALLSTACK 4 29 | # elif defined BSD 30 | # define TRACY_HAS_CALLSTACK 6 31 | # endif 32 | 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /tests/timer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | 4 | const alloc = std.testing.allocator; 5 | 6 | fn test_timer(comptime V: type, comptime is_sim: bool) !void { 7 | const init = struct { 8 | fn start() !void { 9 | const begin = V.time.now(); 10 | 11 | const interval: V.Timespec = std.time.ns_per_ms; 12 | try V.time.sleep(interval); 13 | 14 | const end = V.time.now(); 15 | 16 | if (is_sim) { 17 | // With an mock timer, this should be exact 18 | try std.testing.expectEqual(interval, end - begin); 19 | } else { 20 | // With a real clock, there's not much we can say about the 21 | // timing other than it must be more than the sleep time 22 | try std.testing.expect(end - begin > interval); 23 | } 24 | } 25 | }.start; 26 | 27 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 28 | } 29 | 30 | test "real timer" { 31 | try test_timer(vortex.Vortex, false); 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Brian Gold 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /deps/zig-clap/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jimmi Holst Christensen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyAlloc.cpp: -------------------------------------------------------------------------------- 1 | #ifdef TRACY_ENABLE 2 | 3 | #include 4 | 5 | #include "../common/TracyAlloc.hpp" 6 | #include "../common/TracyForceInline.hpp" 7 | #include "../common/TracyYield.hpp" 8 | 9 | namespace tracy 10 | { 11 | 12 | extern thread_local bool RpThreadInitDone; 13 | extern std::atomic RpInitDone; 14 | extern std::atomic RpInitLock; 15 | 16 | tracy_no_inline static void InitRpmallocPlumbing() 17 | { 18 | const auto done = RpInitDone.load( std::memory_order_acquire ); 19 | if( !done ) 20 | { 21 | int expected = 0; 22 | while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) { expected = 0; YieldThread(); } 23 | const auto done = RpInitDone.load( std::memory_order_acquire ); 24 | if( !done ) 25 | { 26 | rpmalloc_initialize(); 27 | RpInitDone.store( 1, std::memory_order_release ); 28 | } 29 | RpInitLock.store( 0, std::memory_order_release ); 30 | } 31 | rpmalloc_thread_initialize(); 32 | RpThreadInitDone = true; 33 | } 34 | 35 | TRACY_API void InitRpmalloc() 36 | { 37 | if( !RpThreadInitDone ) InitRpmallocPlumbing(); 38 | } 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /deps/zig-clap/clap/args.zig: -------------------------------------------------------------------------------- 1 | const builtin = @import("builtin"); 2 | const std = @import("std"); 3 | 4 | const debug = std.debug; 5 | const heap = std.heap; 6 | const mem = std.mem; 7 | const process = std.process; 8 | const testing = std.testing; 9 | 10 | /// An example of what methods should be implemented on an arg iterator. 11 | pub const ExampleArgIterator = struct { 12 | pub fn next(iter: *ExampleArgIterator) ?[]const u8 { 13 | _ = iter; 14 | return "2"; 15 | } 16 | }; 17 | 18 | /// An argument iterator which iterates over a slice of arguments. 19 | /// This implementation does not allocate. 20 | pub const SliceIterator = struct { 21 | args: []const []const u8, 22 | index: usize = 0, 23 | 24 | pub fn next(iter: *SliceIterator) ?[]const u8 { 25 | if (iter.args.len <= iter.index) 26 | return null; 27 | 28 | defer iter.index += 1; 29 | return iter.args[iter.index]; 30 | } 31 | }; 32 | 33 | test "SliceIterator" { 34 | const args = [_][]const u8{ "A", "BB", "CCC" }; 35 | var iter = SliceIterator{ .args = &args }; 36 | 37 | for (args) |a| 38 | try testing.expectEqualStrings(a, iter.next().?); 39 | 40 | try testing.expectEqual(@as(?[]const u8, null), iter.next()); 41 | } 42 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyStringHelpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSTRINGHELPERS_HPP__ 2 | #define __TRACYSTRINGHELPERS_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include "../common/TracyAlloc.hpp" 8 | 9 | namespace tracy 10 | { 11 | 12 | static inline char* CopyString( const char* src, size_t sz ) 13 | { 14 | assert( strlen( src ) == sz ); 15 | auto dst = (char*)tracy_malloc( sz + 1 ); 16 | memcpy( dst, src, sz ); 17 | dst[sz] = '\0'; 18 | return dst; 19 | } 20 | 21 | static inline char* CopyString( const char* src ) 22 | { 23 | const auto sz = strlen( src ); 24 | auto dst = (char*)tracy_malloc( sz + 1 ); 25 | memcpy( dst, src, sz ); 26 | dst[sz] = '\0'; 27 | return dst; 28 | } 29 | 30 | static inline char* CopyStringFast( const char* src, size_t sz ) 31 | { 32 | assert( strlen( src ) == sz ); 33 | auto dst = (char*)tracy_malloc_fast( sz + 1 ); 34 | memcpy( dst, src, sz ); 35 | dst[sz] = '\0'; 36 | return dst; 37 | } 38 | 39 | static inline char* CopyStringFast( const char* src ) 40 | { 41 | const auto sz = strlen( src ); 42 | auto dst = (char*)tracy_malloc_fast( sz + 1 ); 43 | memcpy( dst, src, sz ); 44 | dst[sz] = '\0'; 45 | return dst; 46 | } 47 | 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /deps/ztracy/README.md: -------------------------------------------------------------------------------- 1 | # ztracy - performance markers for Tracy 0.8.1 2 | 3 | Zig bindings taken from: https://github.com/SpexGuy/Zig-Tracy 4 | 5 | ## Getting started 6 | 7 | Copy `ztracy` folder to a `libs` subdirectory of the root of your project. 8 | 9 | Then in your `build.zig` add: 10 | 11 | ```zig 12 | const std = @import("std"); 13 | const ztracy = @import("libs/ztracy/build.zig"); 14 | 15 | pub fn build(b: *std.build.Builder) void { 16 | ... 17 | const enable_tracy = b.option(bool, "enable-tracy", "Enable Tracy profiler") orelse false; 18 | 19 | const exe_options = b.addOptions(); 20 | exe_options.addOption(bool, "enable_tracy", enable_tracy); 21 | exe.addOptions("build_options", exe_options); 22 | 23 | const options_pkg = exe_options.getPackage("build_options"); 24 | exe.addPackage(ztracy.getPkg(b, options_pkg)); 25 | 26 | ztracy.link(exe, enable_tracy); 27 | } 28 | ``` 29 | 30 | Now in your code you may import and use ztracy. To build your project with Tracy enabled run: 31 | 32 | `zig build -Denable-tracy=true` 33 | 34 | ```zig 35 | const ztracy = @import("ztracy"); 36 | 37 | pub fn main() !void { 38 | { 39 | const tracy_zone = ztracy.ZoneNC(@src(), "Compute Magic", 0x00_ff_00_00); 40 | defer tracy_zone.End(); 41 | ... 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /deps/zig-clap/clap/parsers.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const fmt = std.fmt; 4 | 5 | pub const default = .{ 6 | .string = string, 7 | .str = string, 8 | .u8 = int(u8, 0), 9 | .u16 = int(u16, 0), 10 | .u32 = int(u32, 0), 11 | .u64 = int(u64, 0), 12 | .usize = int(usize, 0), 13 | .i8 = int(i8, 0), 14 | .i16 = int(i16, 0), 15 | .i32 = int(i32, 0), 16 | .i64 = int(i64, 0), 17 | .isize = int(isize, 0), 18 | .f32 = float(f32), 19 | .f64 = float(f64), 20 | }; 21 | 22 | pub fn string(in: []const u8) error{}![]const u8 { 23 | return in; 24 | } 25 | 26 | pub fn int(comptime T: type, comptime radix: u8) fn ([]const u8) fmt.ParseIntError!T { 27 | return struct { 28 | fn parse(in: []const u8) fmt.ParseIntError!T { 29 | return fmt.parseInt(T, in, radix); 30 | } 31 | }.parse; 32 | } 33 | 34 | pub fn float(comptime T: type) fn ([]const u8) fmt.ParseFloatError!T { 35 | return struct { 36 | fn parse(in: []const u8) fmt.ParseFloatError!T { 37 | return fmt.parseFloat(T, in); 38 | } 39 | }.parse; 40 | } 41 | 42 | fn ReturnType(comptime P: type) type { 43 | return @typeInfo(P).Fn.return_type.?; 44 | } 45 | 46 | pub fn Result(comptime P: type) type { 47 | return @typeInfo(ReturnType(P)).ErrorUnion.payload; 48 | } 49 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyAlloc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYALLOC_HPP__ 2 | #define __TRACYALLOC_HPP__ 3 | 4 | #include 5 | 6 | #ifdef TRACY_ENABLE 7 | # include "TracyApi.h" 8 | # include "TracyForceInline.hpp" 9 | # include "../client/tracy_rpmalloc.hpp" 10 | #endif 11 | 12 | namespace tracy 13 | { 14 | 15 | #ifdef TRACY_ENABLE 16 | TRACY_API void InitRpmalloc(); 17 | #endif 18 | 19 | static inline void* tracy_malloc( size_t size ) 20 | { 21 | #ifdef TRACY_ENABLE 22 | InitRpmalloc(); 23 | return rpmalloc( size ); 24 | #else 25 | return malloc( size ); 26 | #endif 27 | } 28 | 29 | static inline void* tracy_malloc_fast( size_t size ) 30 | { 31 | #ifdef TRACY_ENABLE 32 | return rpmalloc( size ); 33 | #else 34 | return malloc( size ); 35 | #endif 36 | } 37 | 38 | static inline void tracy_free( void* ptr ) 39 | { 40 | #ifdef TRACY_ENABLE 41 | InitRpmalloc(); 42 | rpfree( ptr ); 43 | #else 44 | free( ptr ); 45 | #endif 46 | } 47 | 48 | static inline void tracy_free_fast( void* ptr ) 49 | { 50 | #ifdef TRACY_ENABLE 51 | rpfree( ptr ); 52 | #else 53 | free( ptr ); 54 | #endif 55 | } 56 | 57 | static inline void* tracy_realloc( void* ptr, size_t size ) 58 | { 59 | #ifdef TRACY_ENABLE 60 | InitRpmalloc(); 61 | return rprealloc( ptr, size ); 62 | #else 63 | return realloc( ptr, size ); 64 | #endif 65 | } 66 | 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/config.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn Config(comptime Runtime: type) type { 4 | return struct { 5 | const SchedulerConfig = @import("scheduler.zig").Config; 6 | const SyncEventWriter = @import("event.zig").SyncEventWriter; 7 | const PlatformConfig = Runtime.Platform.Config; 8 | 9 | const Self = @This(); 10 | 11 | /// I/O engine configuration 12 | platform: PlatformConfig = PlatformConfig{}, 13 | 14 | /// Scheduler configuration 15 | scheduler: SchedulerConfig = SchedulerConfig{}, 16 | 17 | /// Number of dedicated task threads to spawn. If set to 0, the runtime 18 | /// is configured to be single-threaded, where task execution shares 19 | /// time with the I/O thread. When non-zero, the runtime will spawn that 20 | /// many worker threads to schedule task execution across, and the I/O 21 | /// thread will only process I/O events. 22 | task_threads: usize = 4, 23 | 24 | /// The synchronized `writer' object for writing event records (logs). 25 | event_writer: SyncEventWriter = .{ 26 | .writer = std.io.getStdErr().writer(), 27 | .mutex = std.debug.getStderrMutex(), 28 | }, 29 | 30 | /// The logging level, using the std logging level definitions 31 | log_level: std.log.Level = .info, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /tests/pipe.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const os = std.os; 3 | 4 | const vortex = @import("../vortex.zig"); 5 | 6 | const alloc = std.testing.allocator; 7 | 8 | fn test_pipe(comptime V: type) !void { 9 | const PipePair = V.ipc.PipePair; 10 | const SpawnHandle = V.task.SpawnHandle; 11 | const spawn = V.task.spawn; 12 | 13 | const msg = "this is a test message"; 14 | 15 | const init = struct { 16 | fn reader(pipe: PipePair) !void { 17 | var buf: [msg.len]u8 = undefined; 18 | const c = try pipe.read(&buf, null); 19 | 20 | try std.testing.expectEqual(msg.len, c); 21 | try std.testing.expectEqualSlices(u8, &buf, msg); 22 | } 23 | 24 | fn writer(pipe: PipePair) !void { 25 | const c = try pipe.write(msg, null); 26 | try std.testing.expectEqual(msg.len, c); 27 | } 28 | 29 | fn start() !void { 30 | var pipe = try V.ipc.openPipe(); 31 | defer pipe.deinit(); 32 | 33 | var rd: SpawnHandle(reader) = undefined; 34 | var wr: SpawnHandle(writer) = undefined; 35 | try spawn(&rd, .{pipe}, null); 36 | try spawn(&wr, .{pipe}, null); 37 | try rd.join(); 38 | try wr.join(); 39 | } 40 | }.start; 41 | 42 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 43 | } 44 | 45 | test "pipe" { 46 | try test_pipe(vortex.Vortex); 47 | } 48 | -------------------------------------------------------------------------------- /src/sync/barrier.zig: -------------------------------------------------------------------------------- 1 | //! A simple barrier that self-resets after each sync phase is complete. 2 | //! Based on the original one-time barrier in std.Thread.Futex test case. 3 | 4 | const std = @import("std"); 5 | const builtin = @import("builtin"); 6 | const Atomic = std.atomic.Atomic; 7 | 8 | const clock = @import("../clock.zig"); 9 | const Timespec = clock.Timespec; 10 | 11 | pub fn Barrier(comptime Futex: type) type { 12 | return struct { 13 | const Self = @This(); 14 | 15 | num_tasks: u32, 16 | entered: Atomic(u32) = Atomic(u32).init(0), 17 | phase: Atomic(u32) = Atomic(u32).init(0), // futex variable 18 | 19 | pub fn wait(b: *Self, maybe_timeout: ?Timespec) !void { 20 | const prev_phase = b.phase.load(.Acquire); 21 | const prev_count = b.entered.fetchAdd(1, .AcqRel); 22 | std.debug.assert(prev_count < b.num_tasks); 23 | std.debug.assert(prev_count >= 0); 24 | 25 | // last to arrive wakes all others and sets up next phase 26 | if (prev_count + 1 == b.num_tasks) { 27 | b.entered.store(0, .Release); 28 | b.phase.store(prev_phase + 1, .Release); 29 | Futex.wake(&b.phase, b.num_tasks - 1); 30 | return; 31 | } 32 | 33 | // wait for phase change 34 | while (b.phase.load(.Acquire) == prev_phase) { 35 | try Futex.wait(&b.phase, prev_phase, maybe_timeout); 36 | } 37 | } 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/AUTHORS: -------------------------------------------------------------------------------- 1 | Bartosz Taudul 2 | Kamil Klimek (initial find zone implementation) 3 | Bartosz Szreder (view/worker split) 4 | Arvid Gerstmann (compatibility fixes) 5 | Rokas Kupstys (compatibility fixes, initial CI work, MingW support) 6 | Till Rathmann (DLL support) 7 | Sherief Farouk (compatibility fixes) 8 | Dedmen Miller (find zone bug fixes, improvements) 9 | Michał Cichoń (OSX call stack decoding backport) 10 | Thales Sabino (OpenCL support) 11 | Andrew Depke (Direct3D 12 support) 12 | Simonas Kazlauskas (OSX CI, external bindings) 13 | Jakub Žádník (csvexport utility) 14 | Andrey Voroshilov (multi-DLL fixes) 15 | Benoit Jacob (Android improvements) 16 | David Farrel (Direct3D 11 support) 17 | Terence Rokop (Non-reentrant zones) 18 | Lukas Berbuer (CMake integration) 19 | Xavier Bouchoux (sample data in find zone) 20 | Balazs Kovacsics (Universal Windows Platform) 21 | -------------------------------------------------------------------------------- /deps/zig-clap/example/simple.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("clap"); 2 | const std = @import("std"); 3 | 4 | const debug = std.debug; 5 | const io = std.io; 6 | 7 | pub fn main() !void { 8 | // First we specify what parameters our program can take. 9 | // We can use `parseParamsComptime` to parse a string into an array of `Param(Help)` 10 | const params = comptime clap.parseParamsComptime( 11 | \\-h, --help Display this help and exit. 12 | \\-n, --number An option parameter, which takes a value. 13 | \\-s, --string ... An option parameter which can be specified multiple times. 14 | \\... 15 | \\ 16 | ); 17 | 18 | // Initalize our diagnostics, which can be used for reporting useful errors. 19 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't 20 | // care about the extra information `Diagnostics` provides. 21 | var diag = clap.Diagnostic{}; 22 | var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ 23 | .diagnostic = &diag, 24 | }) catch |err| { 25 | // Report useful error and exit 26 | diag.report(io.getStdErr().writer(), err) catch {}; 27 | return err; 28 | }; 29 | defer res.deinit(); 30 | 31 | if (res.args.help) 32 | debug.print("--help\n", .{}); 33 | if (res.args.number) |n| 34 | debug.print("--number = {}\n", .{n}); 35 | for (res.args.string) |s| 36 | debug.print("--string = {s}\n", .{s}); 37 | for (res.positionals) |pos| 38 | debug.print("{s}\n", .{pos}); 39 | } 40 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2016 Free Software Foundation, Inc. 2 | 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions are 5 | # met: 6 | 7 | # (1) Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | 10 | # (2) Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in 12 | # the documentation and/or other materials provided with the 13 | # distribution. 14 | 15 | # (3) The name of the author may not be used to 16 | # endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | 19 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 28 | # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /deps/zig-clap/example/simple-ex.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("clap"); 2 | const std = @import("std"); 3 | 4 | const debug = std.debug; 5 | const io = std.io; 6 | const process = std.process; 7 | 8 | pub fn main() !void { 9 | // First we specify what parameters our program can take. 10 | // We can use `parseParamsComptime` to parse a string into an array of `Param(Help)` 11 | const params = comptime clap.parseParamsComptime( 12 | \\-h, --help Display this help and exit. 13 | \\-n, --number An option parameter, which takes a value. 14 | \\-s, --string ... An option parameter which can be specified multiple times. 15 | \\... 16 | \\ 17 | ); 18 | 19 | // Declare our own parsers which are used to map the argument strings to other 20 | // types. 21 | const parsers = comptime .{ 22 | .STR = clap.parsers.string, 23 | .FILE = clap.parsers.string, 24 | .INT = clap.parsers.int(usize, 10), 25 | }; 26 | 27 | var diag = clap.Diagnostic{}; 28 | var res = clap.parse(clap.Help, ¶ms, parsers, .{ 29 | .diagnostic = &diag, 30 | }) catch |err| { 31 | diag.report(io.getStdErr().writer(), err) catch {}; 32 | return err; 33 | }; 34 | defer res.deinit(); 35 | 36 | if (res.args.help) 37 | debug.print("--help\n", .{}); 38 | if (res.args.number) |n| 39 | debug.print("--number = {}\n", .{n}); 40 | for (res.args.string) |s| 41 | debug.print("--string = {s}\n", .{s}); 42 | for (res.positionals) |pos| 43 | debug.print("{s}\n", .{pos}); 44 | } 45 | -------------------------------------------------------------------------------- /src/platform/posix_sockets.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const os = std.os; 3 | 4 | // The set of supported POSIX socketcall APIs with an additional (ignored) parameter 5 | // for the loop type. These are included in various loop implementations when 6 | // a simple pass-through to the OS is desired. 7 | 8 | pub fn socket(_: anytype, domain: u32, socket_type: u32, protocol: u32) os.SocketError!os.socket_t { 9 | return os.socket(domain, socket_type, protocol); 10 | } 11 | 12 | pub fn close(_: anytype, sock: os.socket_t) void { 13 | os.close(sock); 14 | } 15 | 16 | pub fn bind(_: anytype, sock: os.socket_t, addr: *const os.sockaddr, len: os.socklen_t) os.BindError!void { 17 | return os.bind(sock, addr, len); 18 | } 19 | 20 | pub fn listen(_: anytype, sock: os.socket_t, backlog: u31) os.ListenError!void { 21 | return os.listen(sock, backlog); 22 | } 23 | 24 | pub fn getsockname(_: anytype, sock: os.socket_t, addr: *os.sockaddr, addrlen: *os.socklen_t) os.GetSockNameError!void { 25 | return os.getsockname(sock, addr, addrlen); 26 | } 27 | 28 | pub fn getpeername(_: anytype, sock: os.socket_t, addr: *os.sockaddr, addrlen: *os.socklen_t) os.GetSockNameError!void { 29 | return os.getpeername(sock, addr, addrlen); 30 | } 31 | 32 | pub fn setsockopt(_: anytype, fd: os.socket_t, level: u32, optname: u32, opt: []const u8) os.SetSockOptError!void { 33 | return os.setsockopt(fd, level, optname, opt); 34 | } 35 | 36 | pub fn getsockopt(_: anytype, fd: i32, level: u32, optname: u32, optval: [*]u8, optlen: *os.socklen_t) usize { 37 | return os.system.getsockopt(fd, level, optname, optval, optlen); 38 | } 39 | -------------------------------------------------------------------------------- /deps/ztracy/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn getPkg(b: *std.build.Builder, options_pkg: std.build.Pkg) std.build.Pkg { 4 | const pkg = std.build.Pkg{ 5 | .name = "ztracy", 6 | .source = .{ .path = thisDir() ++ "/src/ztracy.zig" }, 7 | .dependencies = &[_]std.build.Pkg{options_pkg}, 8 | }; 9 | return b.dupePkg(pkg); 10 | } 11 | 12 | pub const TracyBuildOptions = struct { 13 | fibers: bool = false, 14 | }; 15 | 16 | pub fn link( 17 | exe: *std.build.LibExeObjStep, 18 | enable_tracy: bool, 19 | build_opts: TracyBuildOptions, 20 | ) void { 21 | if (enable_tracy) { 22 | const fibers_flag = if (build_opts.fibers) "-DTRACY_FIBERS" else ""; 23 | 24 | exe.addIncludeDir(thisDir() ++ "/libs/tracy"); 25 | exe.addCSourceFile(thisDir() ++ "/libs/tracy/TracyClient.cpp", &.{ 26 | "-DTRACY_ENABLE", 27 | fibers_flag, 28 | // MinGW doesn't have all the newfangled windows features, 29 | // so we need to pretend to have an older windows version. 30 | "-D_WIN32_WINNT=0x601", 31 | "-fno-sanitize=undefined", 32 | }); 33 | 34 | exe.linkSystemLibrary("c"); 35 | exe.linkSystemLibrary("c++"); 36 | 37 | if (exe.target.isWindows()) { 38 | exe.linkSystemLibrary("Advapi32"); 39 | exe.linkSystemLibrary("User32"); 40 | exe.linkSystemLibrary("Ws2_32"); 41 | exe.linkSystemLibrary("DbgHelp"); 42 | } 43 | } 44 | } 45 | 46 | fn thisDir() []const u8 { 47 | return std.fs.path.dirname(@src().file) orelse "."; 48 | } 49 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/TracyClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Tracy profiler 3 | // ---------------- 4 | // 5 | // For fast integration, compile and 6 | // link with this source file (and none 7 | // other) in your executable (or in the 8 | // main DLL / shared object on multi-DLL 9 | // projects). 10 | // 11 | 12 | // Define TRACY_ENABLE to enable profiler. 13 | 14 | #include "common/TracySystem.cpp" 15 | 16 | #ifdef TRACY_ENABLE 17 | 18 | #ifdef _MSC_VER 19 | # pragma warning(push, 0) 20 | #endif 21 | 22 | #include "common/tracy_lz4.cpp" 23 | #include "client/TracyProfiler.cpp" 24 | #include "client/TracyCallstack.cpp" 25 | #include "client/TracySysTime.cpp" 26 | #include "client/TracySysTrace.cpp" 27 | #include "common/TracySocket.cpp" 28 | #include "client/tracy_rpmalloc.cpp" 29 | #include "client/TracyDxt1.cpp" 30 | #include "client/TracyAlloc.cpp" 31 | 32 | #if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 33 | # include "libbacktrace/alloc.cpp" 34 | # include "libbacktrace/dwarf.cpp" 35 | # include "libbacktrace/fileline.cpp" 36 | # include "libbacktrace/mmapio.cpp" 37 | # include "libbacktrace/posix.cpp" 38 | # include "libbacktrace/sort.cpp" 39 | # include "libbacktrace/state.cpp" 40 | # if TRACY_HAS_CALLSTACK == 4 41 | # include "libbacktrace/macho.cpp" 42 | # else 43 | # include "libbacktrace/elf.cpp" 44 | # endif 45 | # include "common/TracyStackFrames.cpp" 46 | #endif 47 | 48 | #ifdef _MSC_VER 49 | # pragma comment(lib, "ws2_32.lib") 50 | # pragma comment(lib, "dbghelp.lib") 51 | # pragma comment(lib, "advapi32.lib") 52 | # pragma comment(lib, "user32.lib") 53 | # pragma warning(pop) 54 | #endif 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/LICENSE: -------------------------------------------------------------------------------- 1 | Tracy Profiler (https://github.com/wolfpld/tracy) is licensed under the 2 | 3-clause BSD license. 3 | 4 | Copyright (c) 2017-2022, Bartosz Taudul 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of the nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /tests/barrier.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | const Atomic = std.atomic.Atomic; 4 | 5 | const alloc = std.testing.allocator; 6 | 7 | fn test_barrier(comptime V: type) !void { 8 | const Barrier = V.sync.Barrier; 9 | const SpawnHandle = V.task.SpawnHandle; 10 | const spawn = V.task.spawn; 11 | const emit = V.event.emit; 12 | 13 | const ScopedRegistry = V.event.Registry("test.barrier", enum { 14 | iteration_start, 15 | iteration_end, 16 | }); 17 | 18 | const IterationStart = ScopedRegistry.register( 19 | .iteration_start, 20 | .debug, 21 | struct { i: usize, id: usize }, 22 | ); 23 | 24 | const IterationEnd = ScopedRegistry.register( 25 | .iteration_end, 26 | .debug, 27 | struct { i: usize, id: usize }, 28 | ); 29 | 30 | const num_tasks = 8; 31 | const num_phases = 100; 32 | 33 | const init = struct { 34 | fn sync_task(barrier: *Barrier) !void { 35 | var i: usize = 0; 36 | while (i < num_phases) : (i += 1) { 37 | emit(IterationStart, .{ .id = V.task.id(), .i = i }); 38 | try barrier.wait(null); 39 | emit(IterationEnd, .{ .id = V.task.id(), .i = i }); 40 | } 41 | } 42 | 43 | fn start() !void { 44 | var tasks: [num_tasks]SpawnHandle(sync_task) = undefined; 45 | 46 | var barrier = Barrier{ .num_tasks = num_tasks }; 47 | for (tasks) |*t| try spawn(t, .{&barrier}, null); 48 | for (tasks) |*t| _ = try t.join(); 49 | } 50 | }.start; 51 | 52 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 53 | } 54 | 55 | test "barrier" { 56 | try test_barrier(vortex.Vortex); 57 | } 58 | -------------------------------------------------------------------------------- /src/pipe.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const os = std.os; 3 | 4 | const Timespec = @import("clock.zig").Timespec; 5 | const max_time = @import("clock.zig").max_time; 6 | const RuntimeImpl = @import("runtime.zig").RuntimeImpl; 7 | 8 | pub fn Impl(comptime R: type) type { 9 | return struct { 10 | const Self = @This(); 11 | const Runtime = R; 12 | const Platform = R.Platform; 13 | 14 | pub const PipePair = struct { 15 | rt: *Runtime, 16 | rd: os.fd_t, 17 | wr: os.fd_t, 18 | 19 | pub fn init(rt: *Runtime) !PipePair { 20 | // TODO: rt should provide a pipe-creation method so a sim 21 | // version can interpose os, similar to socket methods 22 | const fds = try os.pipe2(os.O.NONBLOCK | os.O.CLOEXEC); 23 | return PipePair{ 24 | .rt = rt, 25 | .rd = fds[0], 26 | .wr = fds[1], 27 | }; 28 | } 29 | 30 | pub fn deinit(pp: *PipePair) void { 31 | os.close(pp.rd); 32 | os.close(pp.wr); 33 | } 34 | 35 | pub fn read( 36 | pp: PipePair, 37 | buf: []u8, 38 | timeout_ns: ?Timespec, 39 | ) !usize { 40 | const timeout = timeout_ns orelse max_time; 41 | return pp.rt.io().read(pp.rd, buf, 0, timeout); 42 | } 43 | 44 | pub fn write( 45 | pp: PipePair, 46 | buf: []const u8, 47 | timeout_ns: ?Timespec, 48 | ) !usize { 49 | const timeout = timeout_ns orelse max_time; 50 | return pp.rt.io().write(pp.wr, buf, 0, timeout); 51 | } 52 | }; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /tests/task.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | 4 | const alloc = std.testing.allocator; 5 | 6 | fn test_spawn(comptime V: type) !void { 7 | const Config = V.Config; 8 | const Timespec = V.Timespec; 9 | const SpawnHandle = V.task.SpawnHandle; 10 | const spawn = V.task.spawn; 11 | const sleep = V.time.sleep; 12 | 13 | const init = struct { 14 | fn child(sleep_time: Timespec) !u32 { 15 | if (sleep_time > 0) try sleep(sleep_time); 16 | 17 | return 42; 18 | } 19 | 20 | fn start(child_sleep: V.Timespec, timeout: Timespec) !void { 21 | var ch: SpawnHandle(child) = undefined; 22 | try spawn(&ch, .{child_sleep}, timeout); 23 | 24 | // If the child_sleep > timeout, this should return TaskTimeout 25 | const res = try ch.join(); 26 | 27 | // Otherwise, we expect to get the answer 28 | try std.testing.expectEqual(@as(u32, 42), res); 29 | } 30 | }.start; 31 | 32 | const timeout = 10 * std.time.ns_per_ms; 33 | const cases = [_]struct { 34 | child_sleep: Timespec, 35 | exp_err: anyerror!void, 36 | }{ 37 | // zig fmt: off 38 | .{ .child_sleep = 0, .exp_err = {} }, 39 | .{ .child_sleep = 2 * timeout, .exp_err = error.TaskTimeout }, 40 | // zig fmt: on 41 | }; 42 | 43 | for (cases) |tc| { 44 | try std.testing.expectEqual( 45 | tc.exp_err, 46 | V.testing.runTest( 47 | alloc, 48 | Config{}, 49 | init, 50 | .{ tc.child_sleep, timeout}, 51 | ), 52 | ); 53 | } 54 | 55 | } 56 | 57 | test "spawn" { 58 | try test_spawn(vortex.Vortex); 59 | } 60 | 61 | // TODO: SimVortex test -------------------------------------------------------------------------------- /tests/cancel-fuzz.zig: -------------------------------------------------------------------------------- 1 | //! 2 | //! Fuzz testing cancellation in the scheduler. This file is a WIP but 3 | //! exposed a number of tricky bugs in the scheduler design. 4 | //! 5 | //! To run a fuzzing campaign: 6 | //! 7 | //! zig build cancel-fuzz # see build.zig for more info 8 | //! mkdir input # only need this once 9 | //! # change '80' in dd count to be 2 * num_tasks 10 | //! dd if=/dev/zero of=input/test.in count=80 bs=1 11 | //! afl-fuzz -i input -o output -t 5000 -- ./zig-out/bin/cancel-fuzz 12 | //! 13 | //! To debug a specific crash: 14 | //! 15 | //! ./zig-out/bin/cancel-debug < output/default/crash/id\:000000....... 16 | //! 17 | //! Future work: 18 | //! - guard against trying to build on Darwin? 19 | //! - add unit tests for every crash/hang 20 | //! - improve ergonomics around variations (autojump, diff options for sizes) 21 | //! 22 | const std = @import("std"); 23 | const assert = std.debug.assert; 24 | 25 | const vx = @import("vortex"); 26 | 27 | fn cMain() callconv(.C) void { 28 | main() catch unreachable; 29 | } 30 | 31 | comptime { 32 | @export(cMain, .{ .name = "main", .linkage = .Strong }); 33 | } 34 | 35 | // The core test logic is in the separate cancel test suite 36 | const TortureTask = @import("cancel.zig").TortureTask; 37 | 38 | pub fn main() !void { 39 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 40 | defer assert(gpa.deinit() == false); 41 | 42 | const alloc = gpa.allocator(); 43 | 44 | const stdin = std.io.getStdIn(); 45 | const data = try stdin.readToEndAlloc(alloc, std.math.maxInt(usize)); 46 | defer alloc.free(data); 47 | 48 | const TT = TortureTask(.{ .fanout = 3, .max_levels = 4 }); 49 | if (data.len != 2 * TT.num_tasks) return; 50 | 51 | try vx.testing.runTest( 52 | alloc, 53 | vx.SingleThreadAutojumpTestConfig, 54 | TT.exec, 55 | .{ 0, 0, data }, 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /demos/one.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vx = @import("vortex").Vortex; 3 | const assert = std.debug.assert; 4 | 5 | pub fn main() !void { 6 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 7 | defer if (@import("builtin").mode == .Debug) { 8 | std.debug.assert(!gpa.deinit()); 9 | }; 10 | 11 | const InitTask = struct { 12 | fn subtask(val: usize) !usize { 13 | vx.event.emit(SubtaskStartEvent, .{ .val = val }); 14 | 15 | const interval = @intCast(vx.Timespec, val); 16 | try vx.time.sleep(interval * std.time.ns_per_ms); 17 | 18 | vx.event.emit(SubtaskFinishEvent, .{}); 19 | return val; 20 | } 21 | 22 | fn start() !void { 23 | vx.event.emit(InitStartEvent, .{}); 24 | 25 | const Handle = vx.task.SpawnHandle(subtask); 26 | var tasks = [1]Handle{undefined} ** 4; 27 | 28 | for (tasks) |*t, i| { 29 | try vx.task.spawn(t, .{i + 1}, null); 30 | } 31 | 32 | var result: usize = 0; 33 | for (tasks) |*t| { 34 | result += try t.join(); 35 | } 36 | 37 | vx.event.emit(InitFinishEvent, .{ .result = result }); 38 | } 39 | }; 40 | 41 | const alloc = gpa.allocator(); 42 | 43 | try vx.init(alloc, vx.DefaultTestConfig); 44 | defer vx.deinit(alloc); 45 | 46 | try vx.run(InitTask.start, .{}); 47 | } 48 | 49 | const ScopedRegistry = vx.event.Registry("demo.one", enum { 50 | subtask_start, 51 | subtask_finish, 52 | init_start, 53 | init_finish, 54 | }); 55 | 56 | const SubtaskStartEvent = ScopedRegistry.register(.subtask_start, .info, struct { val: usize }); 57 | const SubtaskFinishEvent = ScopedRegistry.register(.subtask_finish, .info, struct {}); 58 | const InitStartEvent = ScopedRegistry.register(.init_start, .info, struct {}); 59 | const InitFinishEvent = ScopedRegistry.register(.init_finish, .info, struct { result: usize }); 60 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyThread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYTHREAD_HPP__ 2 | #define __TRACYTHREAD_HPP__ 3 | 4 | #if defined _WIN32 5 | # include 6 | #else 7 | # include 8 | #endif 9 | 10 | #ifdef TRACY_MANUAL_LIFETIME 11 | # include "tracy_rpmalloc.hpp" 12 | #endif 13 | 14 | namespace tracy 15 | { 16 | 17 | #ifdef TRACY_MANUAL_LIFETIME 18 | extern thread_local bool RpThreadInitDone; 19 | #endif 20 | 21 | class ThreadExitHandler 22 | { 23 | public: 24 | ~ThreadExitHandler() 25 | { 26 | #ifdef TRACY_MANUAL_LIFETIME 27 | rpmalloc_thread_finalize(); 28 | RpThreadInitDone = false; 29 | #endif 30 | } 31 | }; 32 | 33 | #if defined _WIN32 34 | 35 | class Thread 36 | { 37 | public: 38 | Thread( void(*func)( void* ptr ), void* ptr ) 39 | : m_func( func ) 40 | , m_ptr( ptr ) 41 | , m_hnd( CreateThread( nullptr, 0, Launch, this, 0, nullptr ) ) 42 | {} 43 | 44 | ~Thread() 45 | { 46 | WaitForSingleObject( m_hnd, INFINITE ); 47 | CloseHandle( m_hnd ); 48 | } 49 | 50 | HANDLE Handle() const { return m_hnd; } 51 | 52 | private: 53 | static DWORD WINAPI Launch( void* ptr ) { ((Thread*)ptr)->m_func( ((Thread*)ptr)->m_ptr ); return 0; } 54 | 55 | void(*m_func)( void* ptr ); 56 | void* m_ptr; 57 | HANDLE m_hnd; 58 | }; 59 | 60 | #else 61 | 62 | class Thread 63 | { 64 | public: 65 | Thread( void(*func)( void* ptr ), void* ptr ) 66 | : m_func( func ) 67 | , m_ptr( ptr ) 68 | { 69 | pthread_create( &m_thread, nullptr, Launch, this ); 70 | } 71 | 72 | ~Thread() 73 | { 74 | pthread_join( m_thread, nullptr ); 75 | } 76 | 77 | pthread_t Handle() const { return m_thread; } 78 | 79 | private: 80 | static void* Launch( void* ptr ) { ((Thread*)ptr)->m_func( ((Thread*)ptr)->m_ptr ); return nullptr; } 81 | void(*m_func)( void* ptr ); 82 | void* m_ptr; 83 | pthread_t m_thread; 84 | }; 85 | 86 | #endif 87 | 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /deps/zig-clap/example/streaming-clap.zig: -------------------------------------------------------------------------------- 1 | const clap = @import("clap"); 2 | const std = @import("std"); 3 | 4 | const debug = std.debug; 5 | const io = std.io; 6 | const process = std.process; 7 | 8 | pub fn main() !void { 9 | const allocator = std.heap.page_allocator; 10 | 11 | // First we specify what parameters our program can take. 12 | const params = [_]clap.Param(u8){ 13 | .{ 14 | .id = 'h', 15 | .names = .{ .short = 'h', .long = "help" }, 16 | }, 17 | .{ 18 | .id = 'n', 19 | .names = .{ .short = 'n', .long = "number" }, 20 | .takes_value = .one, 21 | }, 22 | .{ .id = 'f', .takes_value = .one }, 23 | }; 24 | 25 | var iter = try process.ArgIterator.initWithAllocator(allocator); 26 | defer iter.deinit(); 27 | 28 | // Skip exe argument 29 | _ = iter.next(); 30 | 31 | // Initalize our diagnostics, which can be used for reporting useful errors. 32 | // This is optional. You can also leave the `diagnostic` field unset if you 33 | // don't care about the extra information `Diagnostic` provides. 34 | var diag = clap.Diagnostic{}; 35 | var parser = clap.streaming.Clap(u8, process.ArgIterator){ 36 | .params = ¶ms, 37 | .iter = &iter, 38 | .diagnostic = &diag, 39 | }; 40 | 41 | // Because we use a streaming parser, we have to consume each argument parsed individually. 42 | while (parser.next() catch |err| { 43 | // Report useful error and exit 44 | diag.report(io.getStdErr().writer(), err) catch {}; 45 | return err; 46 | }) |arg| { 47 | // arg.param will point to the parameter which matched the argument. 48 | switch (arg.param.id) { 49 | 'h' => debug.print("Help!\n", .{}), 50 | 'n' => debug.print("--number = {s}\n", .{arg.value.?}), 51 | 52 | // arg.value == null, if arg.param.takes_value == .none. 53 | // Otherwise, arg.value is the value passed with the argument, such as "-a=10" 54 | // or "-a 10". 55 | 'f' => debug.print("{s}\n", .{arg.value.?}), 56 | else => unreachable, 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/futex.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | const Atomic = std.atomic.Atomic; 4 | 5 | const alloc = std.testing.allocator; 6 | 7 | fn test_futex(comptime V: type) !void { 8 | const Timespec = V.Timespec; 9 | const SpawnHandle = V.task.SpawnHandle; 10 | const spawn = V.task.spawn; 11 | const sleep = V.time.sleep; 12 | const Futex = V.sync.Futex; 13 | 14 | const init = struct { 15 | fn child(ptr: *const Atomic(u32), pre_wait_time: Timespec) !void { 16 | try sleep(pre_wait_time); 17 | try Futex.wait(ptr, 0, null); 18 | 19 | try std.testing.expectEqual(@as(u32, 1), ptr.load(.SeqCst)); 20 | } 21 | 22 | fn start(pre_wait_time: Timespec, pre_wake_time: Timespec) !void { 23 | var x = Atomic(u32).init(0); 24 | 25 | // A 1ns timeout on wait should error back 26 | try std.testing.expectError( 27 | error.FutexTimeout, 28 | Futex.wait(&x, 0, 1), 29 | ); 30 | 31 | var ch: SpawnHandle(child) = undefined; 32 | try spawn(&ch, .{ &x, pre_wait_time }, null); 33 | 34 | // force different task orderings 35 | try sleep(pre_wake_time); 36 | 37 | x.store(1, .SeqCst); 38 | Futex.wake(&x, 1); 39 | 40 | _ = try ch.join(); 41 | } 42 | }.start; 43 | 44 | const cases = [_]struct { 45 | pre_wait_time: Timespec, 46 | pre_wake_time: Timespec, 47 | }{ 48 | // zig fmt: off 49 | .{ .pre_wait_time = 0, .pre_wake_time = 0 }, 50 | .{ .pre_wait_time = 0, .pre_wake_time = std.time.ns_per_ms }, 51 | .{ .pre_wait_time = std.time.ns_per_ms, .pre_wake_time = 0 }, 52 | .{ .pre_wait_time = std.time.ns_per_ms, .pre_wake_time = std.time.ns_per_ms }, 53 | // zig fmt: on 54 | }; 55 | 56 | for (cases) |tc| { 57 | try V.testing.runTest( 58 | alloc, 59 | V.DefaultTestConfig, 60 | init, 61 | .{ tc.pre_wait_time, tc.pre_wake_time }, 62 | ); 63 | } 64 | } 65 | 66 | test "futex" { 67 | try test_futex(vortex.Vortex); 68 | } 69 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/filenames.hpp: -------------------------------------------------------------------------------- 1 | /* btest.c -- Filename header for libbacktrace library 2 | Copyright (C) 2012-2018 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #ifndef GCC_VERSION 34 | # define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) 35 | #endif 36 | 37 | #if (GCC_VERSION < 2007) 38 | # define __attribute__(x) 39 | #endif 40 | 41 | #ifndef ATTRIBUTE_UNUSED 42 | # define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) 43 | #endif 44 | 45 | #if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) 46 | # define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') 47 | # define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':') 48 | # define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f)) 49 | #else 50 | # define IS_DIR_SEPARATOR(c) ((c) == '/') 51 | # define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) 52 | #endif 53 | -------------------------------------------------------------------------------- /tests/signal.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const os = std.os; 3 | 4 | const vortex = @import("../vortex.zig"); 5 | 6 | const alloc = std.testing.allocator; 7 | 8 | // NOTE: for some reason, getpid() isn't in std.os, it's only in std.os.linux 9 | extern "c" fn getpid() c_int; 10 | 11 | fn test_signal(comptime V: type) !void { 12 | const SupportedSignal = V.signal.SupportedSignal; 13 | const SignalReader = V.signal.SignalReader; 14 | const SpawnHandle = V.task.SpawnHandle; 15 | const spawn = V.task.spawn; 16 | const emit = V.event.emit; 17 | 18 | const ScopedRegistry = V.event.Registry("test.signal", enum { 19 | signal_raised, 20 | signal_wait, 21 | signal_wake, 22 | }); 23 | 24 | const SignalRaised = ScopedRegistry.register( 25 | .signal_raised, 26 | .debug, 27 | struct { signal: SupportedSignal }, 28 | ); 29 | 30 | const SignalWait = ScopedRegistry.register(.signal_wait, .debug, struct {}); 31 | const SignalWake = ScopedRegistry.register(.signal_wake, .debug, struct {}); 32 | 33 | const init = struct { 34 | fn child(sig: SupportedSignal) !void { 35 | emit(SignalRaised, .{ .signal = sig }); 36 | 37 | // trigger the signal 38 | try os.kill(getpid(), @intCast(u8, @enumToInt(sig))); 39 | 40 | // wait indefinitely 41 | try V.time.sleep(std.math.maxInt(V.Timespec)); 42 | } 43 | 44 | fn sigwait(reader: SignalReader) !void { 45 | emit(SignalWait, .{}); 46 | try reader.wait(null); 47 | emit(SignalWake, .{}); 48 | } 49 | 50 | fn start() !void { 51 | const sig = SupportedSignal.sigterm; 52 | 53 | const sig_reader = try V.signal.register(sig); 54 | 55 | var ch: SpawnHandle(child) = undefined; 56 | var sh: SpawnHandle(sigwait) = undefined; 57 | try spawn(&ch, .{sig}, null); 58 | try spawn(&sh, .{sig_reader}, null); 59 | 60 | switch (try V.task.select(.{ 61 | .child = &ch, 62 | .signal = &sh, 63 | })) { 64 | .child => |err| return err, 65 | .signal => |err| return err, 66 | } 67 | } 68 | }.start; 69 | 70 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 71 | } 72 | 73 | test "signal" { 74 | try test_signal(vortex.Vortex); 75 | } 76 | -------------------------------------------------------------------------------- /deps/zig-clap/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const Builder = std.build.Builder; 4 | 5 | pub fn build(b: *Builder) void { 6 | const mode = b.standardReleaseOptions(); 7 | const target = b.standardTargetOptions(.{}); 8 | 9 | const test_all_step = b.step("test", "Run all tests in all modes."); 10 | inline for (@typeInfo(std.builtin.Mode).Enum.fields) |field| { 11 | const test_mode = @field(std.builtin.Mode, field.name); 12 | const mode_str = @tagName(test_mode); 13 | 14 | const tests = b.addTest("clap.zig"); 15 | tests.setBuildMode(test_mode); 16 | tests.setTarget(target); 17 | 18 | const test_step = b.step("test-" ++ mode_str, "Run all tests in " ++ mode_str ++ "."); 19 | test_step.dependOn(&tests.step); 20 | test_all_step.dependOn(test_step); 21 | } 22 | 23 | const example_step = b.step("examples", "Build examples"); 24 | inline for (.{ 25 | "simple", 26 | "simple-ex", 27 | //"simple-error", 28 | "streaming-clap", 29 | "help", 30 | "usage", 31 | }) |example_name| { 32 | const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig"); 33 | example.addPackagePath("clap", "clap.zig"); 34 | example.setBuildMode(mode); 35 | example.setTarget(target); 36 | example.install(); 37 | example_step.dependOn(&example.step); 38 | } 39 | 40 | const readme_step = b.step("readme", "Remake README."); 41 | const readme = readMeStep(b); 42 | readme.dependOn(example_step); 43 | readme_step.dependOn(readme); 44 | 45 | const all_step = b.step("all", "Build everything and runs all tests"); 46 | all_step.dependOn(test_all_step); 47 | all_step.dependOn(example_step); 48 | all_step.dependOn(readme_step); 49 | 50 | b.default_step.dependOn(all_step); 51 | } 52 | 53 | fn readMeStep(b: *Builder) *std.build.Step { 54 | const s = b.allocator.create(std.build.Step) catch unreachable; 55 | s.* = std.build.Step.init(.custom, "ReadMeStep", b.allocator, struct { 56 | fn make(step: *std.build.Step) anyerror!void { 57 | @setEvalBranchQuota(10000); 58 | _ = step; 59 | const file = try std.fs.cwd().createFile("README.md", .{}); 60 | const stream = file.writer(); 61 | try stream.print(@embedFile("example/README.md.template"), .{ 62 | @embedFile("example/simple.zig"), 63 | @embedFile("example/simple-ex.zig"), 64 | @embedFile("example/streaming-clap.zig"), 65 | @embedFile("example/help.zig"), 66 | @embedFile("example/usage.zig"), 67 | }); 68 | } 69 | }.make); 70 | return s; 71 | } 72 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/state.cpp: -------------------------------------------------------------------------------- 1 | /* state.c -- Create the backtrace state. 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | 38 | #include "backtrace.hpp" 39 | #include "internal.hpp" 40 | 41 | namespace tracy 42 | { 43 | 44 | /* Create the backtrace state. This will then be passed to all the 45 | other routines. */ 46 | 47 | struct backtrace_state * 48 | backtrace_create_state (const char *filename, int threaded, 49 | backtrace_error_callback error_callback, 50 | void *data) 51 | { 52 | struct backtrace_state init_state; 53 | struct backtrace_state *state; 54 | 55 | #ifndef HAVE_SYNC_FUNCTIONS 56 | if (threaded) 57 | { 58 | error_callback (data, "backtrace library does not support threads", 0); 59 | return NULL; 60 | } 61 | #endif 62 | 63 | memset (&init_state, 0, sizeof init_state); 64 | init_state.filename = filename; 65 | init_state.threaded = threaded; 66 | 67 | state = ((struct backtrace_state *) 68 | backtrace_alloc (&init_state, sizeof *state, error_callback, data)); 69 | if (state == NULL) 70 | return NULL; 71 | *state = init_state; 72 | 73 | return state; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /tests/select.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | 4 | const alloc = std.testing.allocator; 5 | 6 | fn test_select(comptime V: type) !void { 7 | const Timespec = V.Timespec; 8 | const SpawnHandle = V.task.SpawnHandle; 9 | const spawn = V.task.spawn; 10 | const select = V.task.select; 11 | const sleep = V.time.sleep; 12 | 13 | const init = struct { 14 | fn child1(timeout: Timespec) !u32 { 15 | if (timeout != 0) try sleep(timeout); 16 | return 42; 17 | } 18 | 19 | fn child2(timeout: Timespec) !void { 20 | if (timeout != 0) try sleep(timeout); 21 | return error.ChildError; 22 | } 23 | 24 | fn start(sleep1: Timespec, sleep2: Timespec) !void { 25 | var t1: SpawnHandle(child1) = undefined; 26 | var t2: SpawnHandle(child2) = undefined; 27 | 28 | try spawn(&t1, .{sleep1}, null); 29 | try spawn(&t2, .{sleep2}, null); 30 | 31 | // Allow sleepX=0 tasks to complete so we exercise paths where a 32 | // finished task before select() is called. 33 | try sleep(0); 34 | 35 | const r = try select(.{ 36 | .ch1 = &t1, 37 | .ch2 = &t2, 38 | }); 39 | 40 | if (sleep1 < sleep2) { 41 | try std.testing.expect(r == .ch1); 42 | try std.testing.expectEqual(@as(u32, 42), try r.ch1); 43 | } else if (sleep1 > sleep2) { 44 | try std.testing.expect(r == .ch2); 45 | try std.testing.expectError(error.ChildError, r.ch2); 46 | } else { 47 | // We can't say much about the result, but include this 48 | // racy test for additional coverage. 49 | } 50 | } 51 | }.start; 52 | 53 | const cases = [_]struct { 54 | sleep1: Timespec, 55 | sleep2: Timespec, 56 | }{ 57 | // zig fmt: off 58 | .{ .sleep1 = std.time.ns_per_ms, .sleep2 = std.time.ns_per_s }, 59 | .{ .sleep1 = std.time.ns_per_s, .sleep2 = std.time.ns_per_ms }, 60 | .{ .sleep1 = 0, .sleep2 = std.time.ns_per_s }, 61 | .{ .sleep1 = std.time.ns_per_s, .sleep2 = 0 }, 62 | .{ .sleep1 = std.time.ns_per_ms, .sleep2 = std.time.ns_per_ms }, 63 | .{ .sleep1 = 0, .sleep2 = 0 }, 64 | // zig fmt: on 65 | }; 66 | 67 | for (cases) |tc| { 68 | try V.testing.runTest( 69 | alloc, 70 | V.DefaultTestConfig, 71 | init, 72 | .{ tc.sleep1, tc.sleep2 }, 73 | ); 74 | } 75 | } 76 | 77 | test "select" { 78 | try test_select(vortex.Vortex); 79 | } -------------------------------------------------------------------------------- /deps/zig-clap/example/README.md.template: -------------------------------------------------------------------------------- 1 | 4 | # zig-clap 5 | 6 | A simple and easy to use command line argument parser library for Zig. 7 | 8 | ## Features 9 | 10 | * Short arguments `-a` 11 | * Chaining `-abc` where `a` and `b` does not take values. 12 | * Long arguments `--long` 13 | * Supports both passing values using spacing and `=` (`-a 100`, `-a=100`) 14 | * Short args also support passing values with no spacing or `=` (`-a100`) 15 | * This all works with chaining (`-ba 100`, `-ba=100`, `-ba100`) 16 | * Supports options that can be specified multiple times (`-e 1 -e 2 -e 3`) 17 | * Print help message from parameter specification. 18 | * Parse help message to parameter specification. 19 | 20 | ## Examples 21 | 22 | ### `clap.parse` 23 | 24 | The simplest way to use this library is to just call the `clap.parse` function. 25 | 26 | ```zig 27 | {s} 28 | ``` 29 | 30 | The result will contain an `args` field and a `positionals` field. `args` will have one field 31 | for each none positional parameter of your program. The name of the field will be the longest 32 | name of the parameter. 33 | 34 | The fields in `args` are typed. The type is based on the name of the value the parameter takes. 35 | Since `--number` takes a `usize` the field `res.args.number` has the type `usize`. 36 | 37 | Note that this is only the case because `clap.parsers.default` has a field called `usize` which 38 | contains a parser that returns `usize`. You can pass in something other than 39 | `clap.parsers.default` if you want some other mapping. 40 | 41 | ```zig 42 | {s} 43 | ``` 44 | 45 | ### `streaming.Clap` 46 | 47 | The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an 48 | `args.Iterator` to provide it with arguments lazily. 49 | 50 | ```zig 51 | {s} 52 | ``` 53 | 54 | Currently, this parser is the only parser that allows an array of `Param` that 55 | is generated at runtime. 56 | 57 | ### `help` 58 | 59 | The `help` prints a simple list of all parameters the program can take. It expects the 60 | `Id` to have a `description` method and an `value` method so that it can provide that 61 | in the output. `HelpOptions` is passed to `help` to control how the help message is 62 | printed. 63 | 64 | ```zig 65 | {s} 66 | ``` 67 | 68 | ``` 69 | $ zig-out/bin/help --help 70 | -h, --help 71 | Display this help and exit. 72 | 73 | -v, --version 74 | Output version information and exit. 75 | ``` 76 | 77 | ### `usage` 78 | 79 | The `usage` prints a small abbreviated version of the help message. It expects the `Id` 80 | to have a `value` method so it can provide that in the output. 81 | 82 | ```zig 83 | {s} 84 | ``` 85 | 86 | ``` 87 | $ zig-out/bin/usage --help 88 | [-hv] [--value ] 89 | ``` 90 | 91 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracySysTime.cpp: -------------------------------------------------------------------------------- 1 | #include "TracySysTime.hpp" 2 | 3 | #ifdef TRACY_HAS_SYSTIME 4 | 5 | # if defined _WIN32 6 | # include 7 | # elif defined __linux__ 8 | # include 9 | # include 10 | # elif defined __APPLE__ 11 | # include 12 | # include 13 | # elif defined BSD 14 | # include 15 | # include 16 | # endif 17 | 18 | namespace tracy 19 | { 20 | 21 | # if defined _WIN32 22 | 23 | static inline uint64_t ConvertTime( const FILETIME& t ) 24 | { 25 | return ( uint64_t( t.dwHighDateTime ) << 32 ) | uint64_t( t.dwLowDateTime ); 26 | } 27 | 28 | void SysTime::ReadTimes() 29 | { 30 | FILETIME idleTime; 31 | FILETIME kernelTime; 32 | FILETIME userTime; 33 | 34 | GetSystemTimes( &idleTime, &kernelTime, &userTime ); 35 | 36 | idle = ConvertTime( idleTime ); 37 | const auto kernel = ConvertTime( kernelTime ); 38 | const auto user = ConvertTime( userTime ); 39 | used = kernel + user; 40 | } 41 | 42 | # elif defined __linux__ 43 | 44 | void SysTime::ReadTimes() 45 | { 46 | uint64_t user, nice, system; 47 | FILE* f = fopen( "/proc/stat", "r" ); 48 | if( f ) 49 | { 50 | int read = fscanf( f, "cpu %" PRIu64 " %" PRIu64 " %" PRIu64" %" PRIu64, &user, &nice, &system, &idle ); 51 | fclose( f ); 52 | if (read == 4) 53 | { 54 | used = user + nice + system; 55 | } 56 | } 57 | } 58 | 59 | # elif defined __APPLE__ 60 | 61 | void SysTime::ReadTimes() 62 | { 63 | host_cpu_load_info_data_t info; 64 | mach_msg_type_number_t cnt = HOST_CPU_LOAD_INFO_COUNT; 65 | host_statistics( mach_host_self(), HOST_CPU_LOAD_INFO, reinterpret_cast( &info ), &cnt ); 66 | used = info.cpu_ticks[CPU_STATE_USER] + info.cpu_ticks[CPU_STATE_NICE] + info.cpu_ticks[CPU_STATE_SYSTEM]; 67 | idle = info.cpu_ticks[CPU_STATE_IDLE]; 68 | } 69 | 70 | # elif defined BSD 71 | 72 | void SysTime::ReadTimes() 73 | { 74 | u_long data[5]; 75 | size_t sz = sizeof( data ); 76 | sysctlbyname( "kern.cp_time", &data, &sz, nullptr, 0 ); 77 | used = data[0] + data[1] + data[2] + data[3]; 78 | idle = data[4]; 79 | } 80 | 81 | #endif 82 | 83 | SysTime::SysTime() 84 | { 85 | ReadTimes(); 86 | } 87 | 88 | float SysTime::Get() 89 | { 90 | const auto oldUsed = used; 91 | const auto oldIdle = idle; 92 | 93 | ReadTimes(); 94 | 95 | const auto diffIdle = idle - oldIdle; 96 | const auto diffUsed = used - oldUsed; 97 | 98 | #if defined _WIN32 99 | return diffUsed == 0 ? -1 : ( diffUsed - diffIdle ) * 100.f / diffUsed; 100 | #elif defined __linux__ || defined __APPLE__ || defined BSD 101 | const auto total = diffUsed + diffIdle; 102 | return total == 0 ? -1 : diffUsed * 100.f / total; 103 | #endif 104 | } 105 | 106 | } 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/clock.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const panic = std.debug.panic; 3 | const target = @import("builtin").target; 4 | 5 | // Nanosecond-resolution 63-bit timers give us operation for 584 years before 6 | // overflow. We use a u63 for compatibility with the signed 64-bit nsec value 7 | // in std.os.timespec 8 | pub const Timespec = u63; 9 | pub const max_time = std.math.maxInt(Timespec); 10 | 11 | pub const DefaultClock = struct { 12 | /// Sample a high-precision monotonic clock that tracks time even while the 13 | /// OS is suspended/sleeping. The returned value should not be used for 14 | /// deriving human-readable wall-clock times. It's useful for measuring 15 | /// forward progress and describing timeouts. 16 | pub fn now(_: *DefaultClock) Timespec { 17 | // Guard against various bugs beyond our control, e.g. 18 | // https://bugzilla.redhat.com/show_bug.cgi?id=448449 19 | // (credit to Joran Dirk Greef in TigerBeetle for this idea and reference) 20 | const Guard = struct { 21 | // Use a threadlocal for the guard so we don't need atomic ops when 22 | // updating it below. 23 | threadlocal var val: Timespec = 0; 24 | }; 25 | 26 | // References for porting to other platforms: 27 | // Clang libcxx std::chrono::steady_clock - https://github.com/llvm/llvm-project/blob/main/libcxx/src/chrono.cpp 28 | // TigerBeetle time.zig - https://github.com/coilhq/tigerbeetle/blob/main/src/time.zig 29 | 30 | const t = switch (target.os.tag) { 31 | // see https://github.com/ziglang/zig/pull/933#discussion_r656021295 32 | .linux => clock_gettime_ns(std.os.CLOCK.BOOTTIME), 33 | 34 | // see https://opensource.apple.com/source/Libc/Libc-1439.40.11/gen/clock_gettime.c.auto.html 35 | .macos, .tvos, .watchos, .ios => clock_gettime_ns(std.os.CLOCK.MONOTONIC_RAW), 36 | 37 | else => @compileError("unsupported platform"), 38 | }; 39 | 40 | if (t < Guard.val) @panic("monotonic clock went backwards"); 41 | Guard.val = t; 42 | 43 | return t; 44 | } 45 | 46 | fn clock_gettime_ns(clk_id: i32) Timespec { 47 | var ts: std.os.timespec = undefined; 48 | std.os.clock_gettime(clk_id, &ts) catch |err| { 49 | panic("clock_gettime({d}) failed: {s}", .{ clk_id, @errorName(err) }); 50 | }; 51 | 52 | var ns: Timespec = 0; 53 | ns += @intCast(Timespec, ts.tv_sec) * std.time.ns_per_s; 54 | ns += @intCast(Timespec, ts.tv_nsec); 55 | return ns; 56 | } 57 | }; 58 | 59 | pub const SimClock = struct { 60 | prev: Timespec = 0, 61 | 62 | pub fn now(self: *SimClock) Timespec { 63 | // An SimClock (used in testing) is advanced in the I/O engine via 64 | // setTime(), and calls to now() see the previous time until the next 65 | // event is processed. 66 | return self.prev; 67 | } 68 | 69 | /// Set the time 70 | pub fn setTime(self: *SimClock, to: Timespec) void { 71 | self.prev = to; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/sync/array_queue.zig: -------------------------------------------------------------------------------- 1 | //! A concurrent, bounded queue backed by an array. Yields the task/thread 2 | //! using `Futex`. 3 | const std = @import("std"); 4 | const builtin = @import("builtin"); 5 | const Atomic = std.atomic.Atomic; 6 | 7 | const RawArrayQueue = @import("raw_array_queue.zig").ConcurrentArrayQueue; 8 | 9 | pub fn ArrayQueue(comptime T: type, comptime Futex: type) type { 10 | return struct { 11 | const Queue = @This(); 12 | const RawQueue = RawArrayQueue(T); 13 | 14 | array: RawQueue, 15 | slots_avail: Atomic(u32), // free slots available for push 16 | items_avail: Atomic(u32), // items available to pop 17 | 18 | pub fn init(alloc: std.mem.Allocator, capacity: u32) !Queue { 19 | return Queue{ 20 | .array = try RawQueue.init(alloc, capacity), 21 | .slots_avail = Atomic(u32).init(capacity), 22 | .items_avail = Atomic(u32).init(0), 23 | }; 24 | } 25 | 26 | pub fn deinit(q: *Queue, alloc: std.mem.Allocator) void { 27 | q.array.deinit(alloc); 28 | } 29 | 30 | pub fn push(q: *Queue, item: T) !void { 31 | while (true) { 32 | // fast-path: non-blocking push 33 | if (q.try_push(item)) return; 34 | 35 | // slow-path: wait for slots_avail to become non-zero, then 36 | // loop around again, relying on Futex to keep waitlist 37 | try Futex.wait(&q.slots_avail, 0, null); 38 | } 39 | } 40 | 41 | pub fn pop(q: *Queue) !T { 42 | while (true) { 43 | // fast-path: non-blocking pop 44 | if (q.try_pop()) |item| return item; 45 | 46 | // slow-path: wait for items_avail to become non-zero, then 47 | // loop around again, relying on Futex to keep waitlist 48 | try Futex.wait(&q.items_avail, 0, null); 49 | } 50 | } 51 | 52 | pub fn try_push(q: *Queue, item: T) bool { 53 | if (q.array.try_push(item)) { 54 | // push success: one fewer slot, one more item 55 | _ = q.slots_avail.fetchSub(1, .Monotonic); 56 | _ = q.items_avail.fetchAdd(1, .Monotonic); 57 | 58 | // wake unconditionally - wake() maintains the list of pending 59 | // waiters and should efficiently know if zero are waiting 60 | Futex.wake(&q.items_avail, 1); 61 | 62 | return true; 63 | } 64 | 65 | return false; 66 | } 67 | 68 | pub fn try_pop(q: *Queue) ?T { 69 | if (q.array.try_pop()) |item| { 70 | // pop success: one fewer item, one more slot 71 | _ = q.items_avail.fetchSub(1, .Monotonic); 72 | _ = q.slots_avail.fetchAdd(1, .Monotonic); 73 | 74 | // wake unconditionally - wake() maintains the list of pending 75 | // waiters and should efficiently know if zero are waiting 76 | Futex.wake(&q.slots_avail, 1); 77 | 78 | return item; 79 | } 80 | 81 | return null; 82 | } 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyFastVector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYFASTVECTOR_HPP__ 2 | #define __TRACYFASTVECTOR_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include "../common/TracyAlloc.hpp" 8 | #include "../common/TracyForceInline.hpp" 9 | 10 | namespace tracy 11 | { 12 | 13 | template 14 | class FastVector 15 | { 16 | public: 17 | using iterator = T*; 18 | using const_iterator = const T*; 19 | 20 | FastVector( size_t capacity ) 21 | : m_ptr( (T*)tracy_malloc( sizeof( T ) * capacity ) ) 22 | , m_write( m_ptr ) 23 | , m_end( m_ptr + capacity ) 24 | { 25 | assert( capacity != 0 ); 26 | } 27 | 28 | FastVector( const FastVector& ) = delete; 29 | FastVector( FastVector&& ) = delete; 30 | 31 | ~FastVector() 32 | { 33 | tracy_free( m_ptr ); 34 | } 35 | 36 | FastVector& operator=( const FastVector& ) = delete; 37 | FastVector& operator=( FastVector&& ) = delete; 38 | 39 | bool empty() const { return m_ptr == m_write; } 40 | size_t size() const { return m_write - m_ptr; } 41 | 42 | T* data() { return m_ptr; } 43 | const T* data() const { return m_ptr; }; 44 | 45 | T* begin() { return m_ptr; } 46 | const T* begin() const { return m_ptr; } 47 | T* end() { return m_write; } 48 | const T* end() const { return m_write; } 49 | 50 | T& front() { assert( !empty() ); return m_ptr[0]; } 51 | const T& front() const { assert( !empty() ); return m_ptr[0]; } 52 | 53 | T& back() { assert( !empty() ); return m_write[-1]; } 54 | const T& back() const { assert( !empty() ); return m_write[-1]; } 55 | 56 | T& operator[]( size_t idx ) { return m_ptr[idx]; } 57 | const T& operator[]( size_t idx ) const { return m_ptr[idx]; } 58 | 59 | T* push_next() 60 | { 61 | if( m_write == m_end ) AllocMore(); 62 | return m_write++; 63 | } 64 | 65 | T* prepare_next() 66 | { 67 | if( m_write == m_end ) AllocMore(); 68 | return m_write; 69 | } 70 | 71 | void commit_next() 72 | { 73 | m_write++; 74 | } 75 | 76 | void clear() 77 | { 78 | m_write = m_ptr; 79 | } 80 | 81 | void swap( FastVector& vec ) 82 | { 83 | const auto ptr1 = m_ptr; 84 | const auto ptr2 = vec.m_ptr; 85 | const auto write1 = m_write; 86 | const auto write2 = vec.m_write; 87 | const auto end1 = m_end; 88 | const auto end2 = vec.m_end; 89 | 90 | m_ptr = ptr2; 91 | vec.m_ptr = ptr1; 92 | m_write = write2; 93 | vec.m_write = write1; 94 | m_end = end2; 95 | vec.m_end = end1; 96 | } 97 | 98 | private: 99 | tracy_no_inline void AllocMore() 100 | { 101 | const auto cap = size_t( m_end - m_ptr ) * 2; 102 | const auto size = size_t( m_write - m_ptr ); 103 | T* ptr = (T*)tracy_malloc( sizeof( T ) * cap ); 104 | memcpy( ptr, m_ptr, size * sizeof( T ) ); 105 | tracy_free_fast( m_ptr ); 106 | m_ptr = ptr; 107 | m_write = m_ptr + size; 108 | m_end = m_ptr + cap; 109 | } 110 | 111 | T* m_ptr; 112 | T* m_write; 113 | T* m_end; 114 | }; 115 | 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyCallstack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYCALLSTACK_HPP__ 2 | #define __TRACYCALLSTACK_HPP__ 3 | 4 | #include "../common/TracyApi.h" 5 | #include "TracyCallstack.h" 6 | 7 | #if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5 8 | # include 9 | #elif TRACY_HAS_CALLSTACK >= 3 10 | # include 11 | #endif 12 | 13 | 14 | #ifdef TRACY_HAS_CALLSTACK 15 | 16 | #include 17 | #include 18 | 19 | #include "../common/TracyAlloc.hpp" 20 | #include "../common/TracyForceInline.hpp" 21 | 22 | namespace tracy 23 | { 24 | 25 | struct CallstackSymbolData 26 | { 27 | const char* file; 28 | uint32_t line; 29 | bool needFree; 30 | uint64_t symAddr; 31 | }; 32 | 33 | struct CallstackEntry 34 | { 35 | const char* name; 36 | const char* file; 37 | uint32_t line; 38 | uint32_t symLen; 39 | uint64_t symAddr; 40 | }; 41 | 42 | struct CallstackEntryData 43 | { 44 | const CallstackEntry* data; 45 | uint8_t size; 46 | const char* imageName; 47 | }; 48 | 49 | CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ); 50 | CallstackSymbolData DecodeCodeAddress( uint64_t ptr ); 51 | const char* DecodeCallstackPtrFast( uint64_t ptr ); 52 | CallstackEntryData DecodeCallstackPtr( uint64_t ptr ); 53 | void InitCallstack(); 54 | const char* GetKernelModulePath( uint64_t addr ); 55 | 56 | #if TRACY_HAS_CALLSTACK == 1 57 | 58 | extern "C" 59 | { 60 | typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long ); 61 | TRACY_API extern ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain; 62 | } 63 | 64 | static tracy_force_inline void* Callstack( int depth ) 65 | { 66 | assert( depth >= 1 && depth < 63 ); 67 | auto trace = (uintptr_t*)tracy_malloc( ( 1 + depth ) * sizeof( uintptr_t ) ); 68 | const auto num = ___tracy_RtlWalkFrameChain( (void**)( trace + 1 ), depth, 0 ); 69 | *trace = num; 70 | return trace; 71 | } 72 | 73 | #elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5 74 | 75 | struct BacktraceState 76 | { 77 | void** current; 78 | void** end; 79 | }; 80 | 81 | static _Unwind_Reason_Code tracy_unwind_callback( struct _Unwind_Context* ctx, void* arg ) 82 | { 83 | auto state = (BacktraceState*)arg; 84 | uintptr_t pc = _Unwind_GetIP( ctx ); 85 | if( pc ) 86 | { 87 | if( state->current == state->end ) return _URC_END_OF_STACK; 88 | *state->current++ = (void*)pc; 89 | } 90 | return _URC_NO_REASON; 91 | } 92 | 93 | static tracy_force_inline void* Callstack( int depth ) 94 | { 95 | assert( depth >= 1 && depth < 63 ); 96 | 97 | auto trace = (uintptr_t*)tracy_malloc( ( 1 + depth ) * sizeof( uintptr_t ) ); 98 | BacktraceState state = { (void**)(trace+1), (void**)(trace+1+depth) }; 99 | _Unwind_Backtrace( tracy_unwind_callback, &state ); 100 | 101 | *trace = (uintptr_t*)state.current - trace + 1; 102 | 103 | return trace; 104 | } 105 | 106 | #elif TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 107 | 108 | static tracy_force_inline void* Callstack( int depth ) 109 | { 110 | assert( depth >= 1 ); 111 | 112 | auto trace = (uintptr_t*)tracy_malloc( ( 1 + (size_t)depth ) * sizeof( uintptr_t ) ); 113 | const auto num = (size_t)backtrace( (void**)(trace+1), depth ); 114 | *trace = num; 115 | 116 | return trace; 117 | } 118 | 119 | #endif 120 | 121 | } 122 | 123 | #endif 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /src/platform.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const target = builtin.target; 4 | 5 | const scheduler = @import("scheduler.zig"); 6 | 7 | const use_reactor = blk: { 8 | const options = @import("build_options"); 9 | const root = @import("root"); 10 | 11 | break :blk if (@hasDecl(root, "force_reactor")) 12 | @as(bool, root.force_reactor) 13 | else 14 | @as(bool, options.force_reactor); 15 | }; 16 | 17 | const DefaultPlatformFactory = switch (target.os.tag) { 18 | .linux => if (!use_reactor) 19 | @import("platform/io_uring.zig").IoUringPlatform 20 | else 21 | @import("platform/io_reactor.zig").ReactorPlatform, 22 | 23 | .macos, 24 | .tvos, 25 | .watchos, 26 | .ios, 27 | => @import("platform/io_reactor.zig").ReactorPlatform, 28 | 29 | else => @compileError("unsupported platform"), 30 | }; 31 | 32 | pub const DefaultPlatform = DefaultPlatformFactory(scheduler.DefaultScheduler); 33 | pub const SimPlatform = @import("platform/io_sim.zig").SimPlatform; 34 | 35 | fn test_sleep(comptime Platform: type) !void { 36 | const Scheduler = Platform.Scheduler; 37 | const Clock = Scheduler.Clock; 38 | const Emitter = @import("event.zig").Emitter(Clock); 39 | const SyncEventWriter = @import("event.zig").SyncEventWriter; 40 | 41 | const InitTask = struct { 42 | sched: *Scheduler, 43 | phase: usize = 0, 44 | 45 | fn start(self: *@This(), platform: *Platform) !void { 46 | suspend { 47 | _ = self.sched.spawnInit(@frame()); 48 | } 49 | defer self.sched.joinInit(); 50 | 51 | self.phase = 1; 52 | try platform.sleep(1); 53 | self.phase = 2; 54 | } 55 | }; 56 | 57 | const alloc = std.testing.allocator; 58 | 59 | var clk = Clock{}; 60 | 61 | const sync_writer = SyncEventWriter{ 62 | .writer = std.io.getStdErr().writer(), 63 | .mutex = std.debug.getStderrMutex(), 64 | }; 65 | 66 | var emitter = Emitter.init(.info, sync_writer); 67 | 68 | var sched = try Scheduler.init(alloc, &clk, &emitter, .{ .max_tasks = 2 }); 69 | defer sched.deinit(alloc); 70 | 71 | var platform = try Platform.init(alloc, Platform.Config{}, &sched, &emitter); 72 | defer platform.deinit(alloc); 73 | 74 | // At first there are no events to complete 75 | try std.testing.expectEqual(@as(usize, 0), try platform.poll(std.time.ns_per_ms)); 76 | 77 | var init_task = InitTask{ .sched = &sched }; 78 | var frame = &async init_task.start(&platform); 79 | 80 | // The init task should have started and suspended immediately 81 | try std.testing.expectEqual(@as(usize, 0), init_task.phase); 82 | 83 | // One task wakeup 84 | try std.testing.expectEqual(@as(usize, 1), sched.tick()); 85 | 86 | // The init task should now be suspended while sleeping 87 | try std.testing.expectEqual(@as(usize, 1), init_task.phase); 88 | 89 | // The platform should have one event to process 90 | try std.testing.expectEqual(@as(usize, 1), try platform.poll(std.time.ns_per_ms)); 91 | 92 | // One task wakeup 93 | try std.testing.expectEqual(@as(usize, 1), sched.tick()); 94 | 95 | // The task should be complete 96 | try std.testing.expectEqual(@as(usize, 2), init_task.phase); 97 | 98 | try nosuspend await frame; 99 | } 100 | 101 | test "DefaultPlatform sleep" { 102 | try test_sleep(DefaultPlatform); 103 | } 104 | 105 | // test "SimPlatform sleep" { 106 | // try test_sleep(SimPlatform); 107 | // } 108 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/posix.cpp: -------------------------------------------------------------------------------- 1 | /* posix.c -- POSIX file I/O routines for the backtrace library. 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "backtrace.hpp" 42 | #include "internal.hpp" 43 | 44 | #ifndef O_BINARY 45 | #define O_BINARY 0 46 | #endif 47 | 48 | #ifndef O_CLOEXEC 49 | #define O_CLOEXEC 0 50 | #endif 51 | 52 | #ifndef FD_CLOEXEC 53 | #define FD_CLOEXEC 1 54 | #endif 55 | 56 | namespace tracy 57 | { 58 | 59 | /* Open a file for reading. */ 60 | 61 | int 62 | backtrace_open (const char *filename, backtrace_error_callback error_callback, 63 | void *data, int *does_not_exist) 64 | { 65 | int descriptor; 66 | 67 | if (does_not_exist != NULL) 68 | *does_not_exist = 0; 69 | 70 | descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC)); 71 | if (descriptor < 0) 72 | { 73 | /* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK 74 | if the file does not exist. We treat lacking permission to 75 | open the file as the file not existing; this case arises when 76 | running the libgo syscall package tests as root. */ 77 | if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES)) 78 | *does_not_exist = 1; 79 | else 80 | error_callback (data, filename, errno); 81 | return -1; 82 | } 83 | 84 | #ifdef HAVE_FCNTL 85 | /* Set FD_CLOEXEC just in case the kernel does not support 86 | O_CLOEXEC. It doesn't matter if this fails for some reason. 87 | FIXME: At some point it should be safe to only do this if 88 | O_CLOEXEC == 0. */ 89 | fcntl (descriptor, F_SETFD, FD_CLOEXEC); 90 | #endif 91 | 92 | return descriptor; 93 | } 94 | 95 | /* Close DESCRIPTOR. */ 96 | 97 | int 98 | backtrace_close (int descriptor, backtrace_error_callback error_callback, 99 | void *data) 100 | { 101 | if (close (descriptor) < 0) 102 | { 103 | error_callback (data, "close", errno); 104 | return 0; 105 | } 106 | return 1; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/mmapio.cpp: -------------------------------------------------------------------------------- 1 | /* mmapio.c -- File views using mmap. 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "backtrace.hpp" 41 | #include "internal.hpp" 42 | 43 | #ifndef HAVE_DECL_GETPAGESIZE 44 | extern int getpagesize (void); 45 | #endif 46 | 47 | #ifndef MAP_FAILED 48 | #define MAP_FAILED ((void *)-1) 49 | #endif 50 | 51 | namespace tracy 52 | { 53 | 54 | /* This file implements file views and memory allocation when mmap is 55 | available. */ 56 | 57 | /* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */ 58 | 59 | int 60 | backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, 61 | int descriptor, off_t offset, uint64_t size, 62 | backtrace_error_callback error_callback, 63 | void *data, struct backtrace_view *view) 64 | { 65 | size_t pagesize; 66 | unsigned int inpage; 67 | off_t pageoff; 68 | void *map; 69 | 70 | if ((uint64_t) (size_t) size != size) 71 | { 72 | error_callback (data, "file size too large", 0); 73 | return 0; 74 | } 75 | 76 | pagesize = getpagesize (); 77 | inpage = offset % pagesize; 78 | pageoff = offset - inpage; 79 | 80 | size += inpage; 81 | size = (size + (pagesize - 1)) & ~ (pagesize - 1); 82 | 83 | map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); 84 | if (map == MAP_FAILED) 85 | { 86 | error_callback (data, "mmap", errno); 87 | return 0; 88 | } 89 | 90 | view->data = (char *) map + inpage; 91 | view->base = map; 92 | view->len = size; 93 | 94 | return 1; 95 | } 96 | 97 | /* Release a view read by backtrace_get_view. */ 98 | 99 | void 100 | backtrace_release_view (struct backtrace_state *state ATTRIBUTE_UNUSED, 101 | struct backtrace_view *view, 102 | backtrace_error_callback error_callback, 103 | void *data) 104 | { 105 | union { 106 | const void *cv; 107 | void *v; 108 | } cc; 109 | 110 | cc.cv = view->base; 111 | if (munmap (cc.v, view->len) < 0) 112 | error_callback (data, "munmap", errno); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/sort.cpp: -------------------------------------------------------------------------------- 1 | /* sort.c -- Sort without allocating memory 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | 38 | #include "backtrace.hpp" 39 | #include "internal.hpp" 40 | 41 | namespace tracy 42 | { 43 | 44 | /* The GNU glibc version of qsort allocates memory, which we must not 45 | do if we are invoked by a signal handler. So provide our own 46 | sort. */ 47 | 48 | static void 49 | swap (char *a, char *b, size_t size) 50 | { 51 | size_t i; 52 | 53 | for (i = 0; i < size; i++, a++, b++) 54 | { 55 | char t; 56 | 57 | t = *a; 58 | *a = *b; 59 | *b = t; 60 | } 61 | } 62 | 63 | void 64 | backtrace_qsort (void *basearg, size_t count, size_t size, 65 | int (*compar) (const void *, const void *)) 66 | { 67 | char *base = (char *) basearg; 68 | size_t i; 69 | size_t mid; 70 | 71 | tail_recurse: 72 | if (count < 2) 73 | return; 74 | 75 | /* The symbol table and DWARF tables, which is all we use this 76 | routine for, tend to be roughly sorted. Pick the middle element 77 | in the array as our pivot point, so that we are more likely to 78 | cut the array in half for each recursion step. */ 79 | swap (base, base + (count / 2) * size, size); 80 | 81 | mid = 0; 82 | for (i = 1; i < count; i++) 83 | { 84 | if ((*compar) (base, base + i * size) > 0) 85 | { 86 | ++mid; 87 | if (i != mid) 88 | swap (base + mid * size, base + i * size, size); 89 | } 90 | } 91 | 92 | if (mid > 0) 93 | swap (base, base + mid * size, size); 94 | 95 | /* Recurse with the smaller array, loop with the larger one. That 96 | ensures that our maximum stack depth is log count. */ 97 | if (2 * mid < count) 98 | { 99 | backtrace_qsort (base, mid, size, compar); 100 | base += (mid + 1) * size; 101 | count -= mid + 1; 102 | goto tail_recurse; 103 | } 104 | else 105 | { 106 | backtrace_qsort (base + (mid + 1) * size, count - (mid + 1), 107 | size, compar); 108 | count = mid; 109 | goto tail_recurse; 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyProtocol.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYPROTOCOL_HPP__ 2 | #define __TRACYPROTOCOL_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace tracy 8 | { 9 | 10 | constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; } 11 | 12 | enum : uint32_t { ProtocolVersion = 56 }; 13 | enum : uint16_t { BroadcastVersion = 2 }; 14 | 15 | using lz4sz_t = uint32_t; 16 | 17 | enum { TargetFrameSize = 256 * 1024 }; 18 | enum { LZ4Size = Lz4CompressBound( TargetFrameSize ) }; 19 | static_assert( LZ4Size <= std::numeric_limits::max(), "LZ4Size greater than lz4sz_t" ); 20 | static_assert( TargetFrameSize * 2 >= 64 * 1024, "Not enough space for LZ4 stream buffer" ); 21 | 22 | enum { HandshakeShibbolethSize = 8 }; 23 | static const char HandshakeShibboleth[HandshakeShibbolethSize] = { 'T', 'r', 'a', 'c', 'y', 'P', 'r', 'f' }; 24 | 25 | enum HandshakeStatus : uint8_t 26 | { 27 | HandshakePending, 28 | HandshakeWelcome, 29 | HandshakeProtocolMismatch, 30 | HandshakeNotAvailable, 31 | HandshakeDropped 32 | }; 33 | 34 | enum { WelcomeMessageProgramNameSize = 64 }; 35 | enum { WelcomeMessageHostInfoSize = 1024 }; 36 | 37 | #pragma pack( 1 ) 38 | 39 | // Must increase left query space after handling! 40 | enum ServerQuery : uint8_t 41 | { 42 | ServerQueryTerminate, 43 | ServerQueryString, 44 | ServerQueryThreadString, 45 | ServerQuerySourceLocation, 46 | ServerQueryPlotName, 47 | ServerQueryFrameName, 48 | ServerQueryParameter, 49 | ServerQueryFiberName, 50 | // Items above are high priority. Split order must be preserved. See IsQueryPrio(). 51 | ServerQueryDisconnect, 52 | ServerQueryCallstackFrame, 53 | ServerQueryExternalName, 54 | ServerQuerySymbol, 55 | ServerQuerySymbolCode, 56 | ServerQueryCodeLocation, 57 | ServerQuerySourceCode, 58 | ServerQueryDataTransfer, 59 | ServerQueryDataTransferPart 60 | }; 61 | 62 | struct ServerQueryPacket 63 | { 64 | ServerQuery type; 65 | uint64_t ptr; 66 | uint32_t extra; 67 | }; 68 | 69 | enum { ServerQueryPacketSize = sizeof( ServerQueryPacket ) }; 70 | 71 | 72 | enum CpuArchitecture : uint8_t 73 | { 74 | CpuArchUnknown, 75 | CpuArchX86, 76 | CpuArchX64, 77 | CpuArchArm32, 78 | CpuArchArm64 79 | }; 80 | 81 | 82 | struct WelcomeFlag 83 | { 84 | enum _t : uint8_t 85 | { 86 | OnDemand = 1 << 0, 87 | IsApple = 1 << 1, 88 | CodeTransfer = 1 << 2, 89 | CombineSamples = 1 << 3, 90 | IdentifySamples = 1 << 4, 91 | }; 92 | }; 93 | 94 | struct WelcomeMessage 95 | { 96 | double timerMul; 97 | int64_t initBegin; 98 | int64_t initEnd; 99 | uint64_t delay; 100 | uint64_t resolution; 101 | uint64_t epoch; 102 | uint64_t exectime; 103 | uint64_t pid; 104 | int64_t samplingPeriod; 105 | uint8_t flags; 106 | uint8_t cpuArch; 107 | char cpuManufacturer[12]; 108 | uint32_t cpuId; 109 | char programName[WelcomeMessageProgramNameSize]; 110 | char hostInfo[WelcomeMessageHostInfoSize]; 111 | }; 112 | 113 | enum { WelcomeMessageSize = sizeof( WelcomeMessage ) }; 114 | 115 | 116 | struct OnDemandPayloadMessage 117 | { 118 | uint64_t frames; 119 | uint64_t currentTime; 120 | }; 121 | 122 | enum { OnDemandPayloadMessageSize = sizeof( OnDemandPayloadMessage ) }; 123 | 124 | 125 | struct BroadcastMessage 126 | { 127 | uint16_t broadcastVersion; 128 | uint16_t listenPort; 129 | uint32_t protocolVersion; 130 | int32_t activeTime; // in seconds 131 | char programName[WelcomeMessageProgramNameSize]; 132 | }; 133 | 134 | enum { BroadcastMessageSize = sizeof( BroadcastMessage ) }; 135 | 136 | #pragma pack() 137 | 138 | } 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyRingBuffer.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tracy 4 | { 5 | 6 | class RingBuffer 7 | { 8 | public: 9 | RingBuffer( unsigned int size, int fd, int id, int cpu = -1 ) 10 | : m_size( size ) 11 | , m_id( id ) 12 | , m_cpu( cpu ) 13 | , m_fd( fd ) 14 | { 15 | const auto pageSize = uint32_t( getpagesize() ); 16 | assert( size >= pageSize ); 17 | assert( __builtin_popcount( size ) == 1 ); 18 | m_mapSize = size + pageSize; 19 | auto mapAddr = mmap( nullptr, m_mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); 20 | if( mapAddr == MAP_FAILED ) 21 | { 22 | TracyDebug( "mmap failed: errno %i (%s)\n", errno, strerror( errno ) ); 23 | m_fd = 0; 24 | m_metadata = nullptr; 25 | close( fd ); 26 | return; 27 | } 28 | m_metadata = (perf_event_mmap_page*)mapAddr; 29 | assert( m_metadata->data_offset == pageSize ); 30 | m_buffer = ((char*)mapAddr) + pageSize; 31 | m_tail = m_metadata->data_tail; 32 | } 33 | 34 | ~RingBuffer() 35 | { 36 | if( m_metadata ) munmap( m_metadata, m_mapSize ); 37 | if( m_fd ) close( m_fd ); 38 | } 39 | 40 | RingBuffer( const RingBuffer& ) = delete; 41 | RingBuffer& operator=( const RingBuffer& ) = delete; 42 | 43 | RingBuffer( RingBuffer&& other ) 44 | { 45 | memcpy( (char*)&other, (char*)this, sizeof( RingBuffer ) ); 46 | m_metadata = nullptr; 47 | m_fd = 0; 48 | } 49 | 50 | RingBuffer& operator=( RingBuffer&& other ) 51 | { 52 | memcpy( (char*)&other, (char*)this, sizeof( RingBuffer ) ); 53 | m_metadata = nullptr; 54 | m_fd = 0; 55 | return *this; 56 | } 57 | 58 | bool IsValid() const { return m_metadata != nullptr; } 59 | int GetId() const { return m_id; } 60 | int GetCpu() const { return m_cpu; } 61 | 62 | void Enable() 63 | { 64 | ioctl( m_fd, PERF_EVENT_IOC_ENABLE, 0 ); 65 | } 66 | 67 | void Read( void* dst, uint64_t offset, uint64_t cnt ) 68 | { 69 | const auto size = m_size; 70 | auto src = ( m_tail + offset ) % size; 71 | if( src + cnt <= size ) 72 | { 73 | memcpy( dst, m_buffer + src, cnt ); 74 | } 75 | else 76 | { 77 | const auto s0 = size - src; 78 | const auto buf = m_buffer; 79 | memcpy( dst, buf + src, s0 ); 80 | memcpy( (char*)dst + s0, buf, cnt - s0 ); 81 | } 82 | } 83 | 84 | void Advance( uint64_t cnt ) 85 | { 86 | m_tail += cnt; 87 | StoreTail(); 88 | } 89 | 90 | bool CheckTscCaps() const 91 | { 92 | return m_metadata->cap_user_time_zero; 93 | } 94 | 95 | int64_t ConvertTimeToTsc( int64_t timestamp ) const 96 | { 97 | if( !m_metadata->cap_user_time_zero ) return 0; 98 | const auto time = timestamp - m_metadata->time_zero; 99 | const auto quot = time / m_metadata->time_mult; 100 | const auto rem = time % m_metadata->time_mult; 101 | return ( quot << m_metadata->time_shift ) + ( rem << m_metadata->time_shift ) / m_metadata->time_mult; 102 | } 103 | 104 | uint64_t LoadHead() const 105 | { 106 | return std::atomic_load_explicit( (const volatile std::atomic*)&m_metadata->data_head, std::memory_order_acquire ); 107 | } 108 | 109 | uint64_t GetTail() const 110 | { 111 | return m_tail; 112 | } 113 | 114 | private: 115 | void StoreTail() 116 | { 117 | std::atomic_store_explicit( (volatile std::atomic*)&m_metadata->data_tail, m_tail, std::memory_order_release ); 118 | } 119 | 120 | unsigned int m_size; 121 | uint64_t m_tail; 122 | char* m_buffer; 123 | int m_id; 124 | int m_cpu; 125 | perf_event_mmap_page* m_metadata; 126 | 127 | size_t m_mapSize; 128 | int m_fd; 129 | }; 130 | 131 | } 132 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracySocket.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSOCKET_HPP__ 2 | #define __TRACYSOCKET_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include "TracyForceInline.hpp" 8 | 9 | struct addrinfo; 10 | struct sockaddr; 11 | 12 | namespace tracy 13 | { 14 | 15 | #ifdef _WIN32 16 | void InitWinSock(); 17 | #endif 18 | 19 | class Socket 20 | { 21 | public: 22 | Socket(); 23 | Socket( int sock ); 24 | ~Socket(); 25 | 26 | bool Connect( const char* addr, uint16_t port ); 27 | bool ConnectBlocking( const char* addr, uint16_t port ); 28 | void Close(); 29 | 30 | int Send( const void* buf, int len ); 31 | int GetSendBufSize(); 32 | 33 | int ReadUpTo( void* buf, int len, int timeout ); 34 | bool Read( void* buf, int len, int timeout ); 35 | 36 | template 37 | bool Read( void* buf, int len, int timeout, ShouldExit exitCb ) 38 | { 39 | auto cbuf = (char*)buf; 40 | while( len > 0 ) 41 | { 42 | if( exitCb() ) return false; 43 | if( !ReadImpl( cbuf, len, timeout ) ) return false; 44 | } 45 | return true; 46 | } 47 | 48 | bool ReadRaw( void* buf, int len, int timeout ); 49 | bool HasData(); 50 | bool IsValid() const; 51 | 52 | Socket( const Socket& ) = delete; 53 | Socket( Socket&& ) = delete; 54 | Socket& operator=( const Socket& ) = delete; 55 | Socket& operator=( Socket&& ) = delete; 56 | 57 | private: 58 | int RecvBuffered( void* buf, int len, int timeout ); 59 | int Recv( void* buf, int len, int timeout ); 60 | 61 | bool ReadImpl( char*& buf, int& len, int timeout ); 62 | 63 | char* m_buf; 64 | char* m_bufPtr; 65 | std::atomic m_sock; 66 | int m_bufLeft; 67 | 68 | struct addrinfo *m_res; 69 | struct addrinfo *m_ptr; 70 | int m_connSock; 71 | }; 72 | 73 | class ListenSocket 74 | { 75 | public: 76 | ListenSocket(); 77 | ~ListenSocket(); 78 | 79 | bool Listen( uint16_t port, int backlog ); 80 | Socket* Accept(); 81 | void Close(); 82 | 83 | ListenSocket( const ListenSocket& ) = delete; 84 | ListenSocket( ListenSocket&& ) = delete; 85 | ListenSocket& operator=( const ListenSocket& ) = delete; 86 | ListenSocket& operator=( ListenSocket&& ) = delete; 87 | 88 | private: 89 | int m_sock; 90 | }; 91 | 92 | class UdpBroadcast 93 | { 94 | public: 95 | UdpBroadcast(); 96 | ~UdpBroadcast(); 97 | 98 | bool Open( const char* addr, uint16_t port ); 99 | void Close(); 100 | 101 | int Send( uint16_t port, const void* data, int len ); 102 | 103 | UdpBroadcast( const UdpBroadcast& ) = delete; 104 | UdpBroadcast( UdpBroadcast&& ) = delete; 105 | UdpBroadcast& operator=( const UdpBroadcast& ) = delete; 106 | UdpBroadcast& operator=( UdpBroadcast&& ) = delete; 107 | 108 | private: 109 | int m_sock; 110 | uint32_t m_addr; 111 | }; 112 | 113 | class IpAddress 114 | { 115 | public: 116 | IpAddress(); 117 | ~IpAddress(); 118 | 119 | void Set( const struct sockaddr& addr ); 120 | 121 | uint32_t GetNumber() const { return m_number; } 122 | const char* GetText() const { return m_text; } 123 | 124 | IpAddress( const IpAddress& ) = delete; 125 | IpAddress( IpAddress&& ) = delete; 126 | IpAddress& operator=( const IpAddress& ) = delete; 127 | IpAddress& operator=( IpAddress&& ) = delete; 128 | 129 | private: 130 | uint32_t m_number; 131 | char m_text[17]; 132 | }; 133 | 134 | class UdpListen 135 | { 136 | public: 137 | UdpListen(); 138 | ~UdpListen(); 139 | 140 | bool Listen( uint16_t port ); 141 | void Close(); 142 | 143 | const char* Read( size_t& len, IpAddress& addr, int timeout ); 144 | 145 | UdpListen( const UdpListen& ) = delete; 146 | UdpListen( UdpListen&& ) = delete; 147 | UdpListen& operator=( const UdpListen& ) = delete; 148 | UdpListen& operator=( UdpListen&& ) = delete; 149 | 150 | private: 151 | int m_sock; 152 | }; 153 | 154 | } 155 | 156 | #endif 157 | -------------------------------------------------------------------------------- /tests/tcp.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | 4 | const alloc = std.testing.allocator; 5 | 6 | fn test_hello(comptime V: type) !void { 7 | const Address = std.net.Address; 8 | 9 | const State = std.atomic.Atomic(enum(u8) { 10 | start, 11 | listening, 12 | done, 13 | }); 14 | 15 | const init = struct { 16 | fn server(addr: Address, state: *State) !void { 17 | const io_timeout = std.time.ns_per_s; 18 | 19 | var l = try V.net.startTcpListener(addr, 64); 20 | defer l.close(); 21 | 22 | state.store(.listening, .Release); 23 | 24 | var stream = try l.accept(io_timeout); 25 | defer stream.close(); 26 | 27 | try std.testing.expect(stream.fd > 0); 28 | 29 | // Should the recv hit an error, we still want to signal to the 30 | // client that we're done, to ensure we crash rather than hang. 31 | // We have to be careful of the order of defers, so this barrier 32 | // runs before the sockets are closed. 33 | defer state.store(.done, .Release); 34 | 35 | var buf: [32]u8 = undefined; 36 | const nb = try stream.recv(&buf, io_timeout); 37 | 38 | try std.testing.expect(std.mem.eql(u8, "Hello, world", buf[0..nb])); 39 | } 40 | 41 | fn client(addr: Address, state: *State) !void { 42 | const io_timeout = std.time.ns_per_s; 43 | 44 | while (state.load(.Acquire) == .start) { 45 | try V.time.sleep(std.time.ns_per_ms); 46 | } 47 | 48 | var stream = try V.net.openTcpStream(addr, io_timeout); 49 | defer stream.close(); 50 | 51 | try std.testing.expect(stream.fd > 0); 52 | 53 | const msg = "Hello, world"; 54 | const nb = try stream.send(msg, io_timeout); 55 | 56 | try std.testing.expectEqual(msg.len, nb); 57 | 58 | // If the client exits, we tear down the connection and the server 59 | // may not have received the reply. So wait. 60 | while (state.load(.Acquire) != .done) { 61 | try V.time.sleep(std.time.ns_per_ms); 62 | } 63 | } 64 | 65 | fn start(timeout: V.Timespec) !void { 66 | var state = State.init(.start); 67 | var srv: V.task.SpawnHandle(server) = undefined; 68 | var cli: V.task.SpawnHandle(client) = undefined; 69 | 70 | const addr = try V.testing.unusedTcpPort(); 71 | 72 | try V.task.spawn(&srv, .{ addr, &state }, timeout); 73 | defer srv.join() catch |err| { 74 | std.debug.panic("Server error: {s}\n", .{@errorName(err)}); 75 | }; 76 | 77 | try V.task.spawn(&cli, .{ addr, &state }, timeout); 78 | defer cli.join() catch |err| { 79 | std.debug.panic("Client error: {s}\n", .{@errorName(err)}); 80 | }; 81 | } 82 | }.start; 83 | 84 | try V.testing.runTest( 85 | alloc, 86 | V.DefaultTestConfig, 87 | init, 88 | .{std.time.ns_per_s}, 89 | ); 90 | } 91 | 92 | test "hello" { 93 | try test_hello(vortex.Vortex); 94 | } 95 | 96 | fn test_timeout(comptime V: type) !void { 97 | const init = struct { 98 | fn start(timeout: V.Timespec) !void { 99 | const addr = try V.testing.unusedTcpPort(); 100 | 101 | var l = try V.net.startTcpListener(addr, 64); 102 | defer l.close(); 103 | 104 | _ = try l.accept(timeout); 105 | 106 | unreachable; // Timeout should have been triggered 107 | } 108 | }.start; 109 | 110 | try std.testing.expectError( 111 | error.IoCanceled, 112 | V.testing.runTest( 113 | alloc, 114 | V.DefaultTestConfig, 115 | init, 116 | .{std.time.ns_per_ms}, 117 | ), 118 | ); 119 | } 120 | 121 | test "timeout" { 122 | try test_timeout(vortex.Vortex); 123 | } 124 | -------------------------------------------------------------------------------- /tests/channel.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const vortex = @import("../vortex.zig"); 3 | 4 | const alloc = std.testing.allocator; 5 | 6 | fn test_spsc(comptime V: type) !void { 7 | const Channel = V.sync.Channel; 8 | const SpawnHandle = V.task.SpawnHandle; 9 | const spawn = V.task.spawn; 10 | 11 | const chan_size = 16; 12 | const num_messages = 32; 13 | 14 | const init = struct { 15 | fn sender(chan: *Channel(usize)) !void { 16 | var i: usize = 0; 17 | while (i < num_messages) : (i += 1) { 18 | try chan.push(i); 19 | } 20 | } 21 | 22 | fn receiver(chan: *Channel(usize)) !void { 23 | var i: usize = 0; 24 | while (i < num_messages) : (i += 1) { 25 | const v = try chan.pop(); 26 | try std.testing.expectEqual(i, v); 27 | } 28 | } 29 | 30 | fn start() !void { 31 | var c = try Channel(usize).init(alloc, chan_size); 32 | defer c.deinit(alloc); 33 | 34 | var sh: SpawnHandle(sender) = undefined; 35 | var rh: SpawnHandle(receiver) = undefined; 36 | try spawn(&sh, .{&c}, null); 37 | try spawn(&rh, .{&c}, null); 38 | try sh.join(); 39 | try rh.join(); 40 | } 41 | }.start; 42 | 43 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 44 | } 45 | 46 | fn test_channel( 47 | comptime V: type, 48 | comptime chan_size: comptime_int, 49 | comptime num_senders: comptime_int, 50 | comptime num_receivers: comptime_int, 51 | comptime num_messages: comptime_int, 52 | ) !void { 53 | const Channel = V.sync.Channel; 54 | const SpawnHandle = V.task.SpawnHandle; 55 | const spawn = V.task.spawn; 56 | 57 | const init = struct { 58 | fn sender(chan: *Channel(usize), idx: usize) !void { 59 | var i: usize = idx; 60 | while (i < num_messages) : (i += num_senders) { 61 | try chan.push(i); 62 | } 63 | } 64 | 65 | fn receiver(chan: *Channel(usize)) !usize { 66 | var sum: usize = 0; 67 | var i: usize = 0; 68 | while (i < num_messages) : (i += num_receivers) { 69 | sum += try chan.pop(); 70 | } 71 | return sum; 72 | } 73 | 74 | fn start() !void { 75 | var c = try Channel(usize).init(alloc, chan_size); 76 | defer c.deinit(alloc); 77 | 78 | var senders: [num_senders]SpawnHandle(sender) = undefined; 79 | for (senders) |*sh, i| { 80 | spawn(sh, .{ &c, i }, null) catch |err| 81 | std.debug.panic("Error spawning sender: {}", .{err}); 82 | } 83 | 84 | var receivers: [num_receivers]SpawnHandle(receiver) = undefined; 85 | for (receivers) |*rh| { 86 | spawn(rh, .{&c}, null) catch |err| 87 | std.debug.panic("Error spawning receiver: {}", .{err}); 88 | } 89 | 90 | var sum: usize = 0; 91 | for (senders) |*sh| { 92 | sh.join() catch |err| 93 | std.debug.panic("Error joining sender: {}", .{err}); 94 | } 95 | for (receivers) |*rh| { 96 | sum += rh.join() catch |err| 97 | std.debug.panic("Error joining receiver: {}", .{err}); 98 | } 99 | 100 | try std.testing.expectEqual( 101 | @as(usize, num_messages * (num_messages - 1) / 2), 102 | sum, 103 | ); 104 | } 105 | }.start; 106 | 107 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 108 | } 109 | 110 | test "channel" { 111 | // also validates ordering on the channel 112 | try test_spsc(vortex.Vortex); 113 | 114 | try test_channel(vortex.Vortex, 16, 1, 128, 1024); // SPMC 115 | try test_channel(vortex.Vortex, 16, 1, 128, 1024); // SPMC 116 | try test_channel(vortex.Vortex, 16, 128, 1, 1024); // MPSC 117 | try test_channel(vortex.Vortex, 16, 128, 128, 1024); // MPMC 118 | } 119 | -------------------------------------------------------------------------------- /src/platform/cancel_queue.zig: -------------------------------------------------------------------------------- 1 | //! A CancelQueue is a singly linked list that supports two operations: 2 | //! `insert` a new item and `unlink` the entire list. Both are thread-safe, 3 | //! with `unlink` enabling a caller to traverse the list without holding a 4 | //! lock. Nodes in the list are intrusive and ownership over the node is 5 | //! retained by the caller. Thus, the lifetime of a node must outlive its 6 | //! life on the list. 7 | //! 8 | //! This data structure is designed to model the cancellation of I/O operations 9 | //! in multiple platform backends, where cancellation may be requested from 10 | //! any thread, but must be processed by a specific thread where the I/O 11 | //! originated. The lifetime of a "Node" (IoOperation) must outlive the 12 | //! cancellation duration. 13 | 14 | const std = @import("std"); 15 | 16 | /// A `CancelQueue` is an intrusive structure over `Node` types, where 17 | /// `link_name` is the "next" pointer in Node. 18 | pub fn CancelQueue(comptime Node: type, comptime link_name: []const u8) type { 19 | if (!@hasField(Node, link_name)) 20 | @compileError(@typeName(Node) ++ " does not contain link `" ++ link_name ++ "`"); 21 | 22 | return struct { 23 | const Queue = @This(); 24 | 25 | mutex: std.Thread.Mutex = .{}, 26 | head: ?*Node = null, 27 | tail: ?*Node = null, 28 | 29 | /// Inserts a new `node` onto CancelQueue `q`. 30 | pub fn insert(q: *Queue, node: *Node) void { 31 | q.mutex.lock(); 32 | defer q.mutex.unlock(); 33 | 34 | if (q.head == null) q.head = node; 35 | if (q.tail) |tail| { 36 | @field(tail, link_name) = node; 37 | } 38 | q.tail = node; 39 | } 40 | 41 | /// Unlinks entire chain, returning an iterator 42 | pub fn unlink(q: *Queue) Iterator { 43 | // TODO: measure impact of this optimization 44 | if (@atomicLoad(?*Node, &q.head, .Monotonic) == null) { 45 | return Iterator{ .cur = null }; 46 | } 47 | 48 | q.mutex.lock(); 49 | defer q.mutex.unlock(); 50 | 51 | const head = q.head; 52 | q.head = null; 53 | q.tail = null; 54 | 55 | return Iterator{ 56 | .cur = head, 57 | }; 58 | } 59 | 60 | const Iterator = struct { 61 | prev: ?*Node = null, 62 | cur: ?*Node, 63 | 64 | pub fn get(it: *Iterator) ?*Node { 65 | return it.cur; 66 | } 67 | 68 | pub fn next(it: *Iterator) ?*Node { 69 | it.prev = it.cur orelse return null; 70 | it.cur = @field(it.prev.?, link_name); 71 | return it.cur; 72 | } 73 | }; 74 | }; 75 | } 76 | 77 | test "CancelQueue" { 78 | const alloc = std.testing.allocator; 79 | 80 | const TestNode = struct { 81 | canceled: bool = false, 82 | next: ?*@This() = null, 83 | }; 84 | 85 | const Queue = CancelQueue(TestNode, "next"); 86 | 87 | const Worker = struct { 88 | fn entry(queue: *Queue, num_iter: usize) void { 89 | // items to enqueue must outlive cancellation 90 | var items = alloc.alloc(TestNode, num_iter) catch unreachable; 91 | defer alloc.free(items); 92 | 93 | for (items) |*it| { 94 | it.* = TestNode{}; 95 | queue.insert(it); 96 | } 97 | 98 | // spin while waiting for cancelation to finish 99 | for (items) |*it| { 100 | while (@atomicLoad(bool, &it.canceled, .Monotonic) == false) {} 101 | } 102 | } 103 | }; 104 | 105 | const num_threads = 4; 106 | const num_iter = 1000; 107 | 108 | var workers = try alloc.alloc(std.Thread, num_threads); 109 | defer alloc.free(workers); 110 | 111 | var queue = Queue{}; 112 | 113 | for (workers) |*w| { 114 | w.* = try std.Thread.spawn( 115 | .{}, 116 | Worker.entry, 117 | .{ &queue, num_iter }, 118 | ); 119 | } 120 | 121 | var count: usize = 0; 122 | while (count < num_iter * num_threads) { 123 | var to_cancel = queue.unlink(); 124 | while (to_cancel.get()) |item| : (_ = to_cancel.next()) { 125 | @atomicStore(bool, &item.canceled, true, .Monotonic); 126 | count += 1; 127 | } 128 | } 129 | 130 | for (workers) |*w| { 131 | w.join(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracyStackFrames.cpp: -------------------------------------------------------------------------------- 1 | #include "TracyStackFrames.hpp" 2 | 3 | namespace tracy 4 | { 5 | 6 | const char* s_tracyStackFrames_[] = { 7 | "tracy::Callstack", 8 | "tracy::Callstack(int)", 9 | "tracy::GpuCtxScope::{ctor}", 10 | "tracy::Profiler::SendCallstack", 11 | "tracy::Profiler::SendCallstack(int)", 12 | "tracy::Profiler::SendCallstack(int, unsigned long)", 13 | "tracy::Profiler::MemAllocCallstack", 14 | "tracy::Profiler::MemAllocCallstack(void const*, unsigned long, int)", 15 | "tracy::Profiler::MemFreeCallstack", 16 | "tracy::Profiler::MemFreeCallstack(void const*, int)", 17 | "tracy::ScopedZone::{ctor}", 18 | "tracy::ScopedZone::ScopedZone(tracy::SourceLocationData const*, int, bool)", 19 | "tracy::Profiler::Message", 20 | nullptr 21 | }; 22 | 23 | const char** s_tracyStackFrames = s_tracyStackFrames_; 24 | 25 | const StringMatch s_tracySkipSubframes_[] = { 26 | { "/include/arm_neon.h", 19 }, 27 | { "/include/adxintrin.h", 20 }, 28 | { "/include/ammintrin.h", 20 }, 29 | { "/include/amxbf16intrin.h", 24 }, 30 | { "/include/amxint8intrin.h", 24 }, 31 | { "/include/amxtileintrin.h", 24 }, 32 | { "/include/avx2intrin.h", 21 }, 33 | { "/include/avx5124fmapsintrin.h", 29 }, 34 | { "/include/avx5124vnniwintrin.h", 29 }, 35 | { "/include/avx512bf16intrin.h", 27 }, 36 | { "/include/avx512bf16vlintrin.h", 29 }, 37 | { "/include/avx512bitalgintrin.h", 29 }, 38 | { "/include/avx512bwintrin.h", 25 }, 39 | { "/include/avx512cdintrin.h", 25 }, 40 | { "/include/avx512dqintrin.h", 25 }, 41 | { "/include/avx512erintrin.h", 25 }, 42 | { "/include/avx512fintrin.h", 24 }, 43 | { "/include/avx512ifmaintrin.h", 27 }, 44 | { "/include/avx512ifmavlintrin.h", 29 }, 45 | { "/include/avx512pfintrin.h", 25 }, 46 | { "/include/avx512vbmi2intrin.h", 28 }, 47 | { "/include/avx512vbmi2vlintrin.h", 30 }, 48 | { "/include/avx512vbmiintrin.h", 27 }, 49 | { "/include/avx512vbmivlintrin.h", 29 }, 50 | { "/include/avx512vlbwintrin.h", 27 }, 51 | { "/include/avx512vldqintrin.h", 27 }, 52 | { "/include/avx512vlintrin.h", 25 }, 53 | { "/include/avx512vnniintrin.h", 27 }, 54 | { "/include/avx512vnnivlintrin.h", 29 }, 55 | { "/include/avx512vp2intersectintrin.h", 35 }, 56 | { "/include/avx512vp2intersectvlintrin.h", 37 }, 57 | { "/include/avx512vpopcntdqintrin.h", 32 }, 58 | { "/include/avx512vpopcntdqvlintrin.h", 34 }, 59 | { "/include/avxintrin.h", 20 }, 60 | { "/include/avxvnniintrin.h", 24 }, 61 | { "/include/bmi2intrin.h", 21 }, 62 | { "/include/bmiintrin.h", 20 }, 63 | { "/include/bmmintrin.h", 20 }, 64 | { "/include/cetintrin.h", 20 }, 65 | { "/include/cldemoteintrin.h", 25 }, 66 | { "/include/clflushoptintrin.h", 27 }, 67 | { "/include/clwbintrin.h", 21 }, 68 | { "/include/clzerointrin.h", 23 }, 69 | { "/include/emmintrin.h", 20 }, 70 | { "/include/enqcmdintrin.h", 23 }, 71 | { "/include/f16cintrin.h", 21 }, 72 | { "/include/fma4intrin.h", 21 }, 73 | { "/include/fmaintrin.h", 20 }, 74 | { "/include/fxsrintrin.h", 21 }, 75 | { "/include/gfniintrin.h", 21 }, 76 | { "/include/hresetintrin.h", 23 }, 77 | { "/include/ia32intrin.h", 21 }, 78 | { "/include/immintrin.h", 20 }, 79 | { "/include/keylockerintrin.h", 26 }, 80 | { "/include/lwpintrin.h", 20 }, 81 | { "/include/lzcntintrin.h", 22 }, 82 | { "/include/mmintrin.h", 19 }, 83 | { "/include/movdirintrin.h", 23 }, 84 | { "/include/mwaitxintrin.h", 23 }, 85 | { "/include/nmmintrin.h", 20 }, 86 | { "/include/pconfigintrin.h", 24 }, 87 | { "/include/pkuintrin.h", 20 }, 88 | { "/include/pmmintrin.h", 20 }, 89 | { "/include/popcntintrin.h", 23 }, 90 | { "/include/prfchwintrin.h", 23 }, 91 | { "/include/rdseedintrin.h", 23 }, 92 | { "/include/rtmintrin.h", 20 }, 93 | { "/include/serializeintrin.h", 26 }, 94 | { "/include/sgxintrin.h", 20 }, 95 | { "/include/shaintrin.h", 20 }, 96 | { "/include/smmintrin.h", 20 }, 97 | { "/include/tbmintrin.h", 20 }, 98 | { "/include/tmmintrin.h", 20 }, 99 | { "/include/tsxldtrkintrin.h", 25 }, 100 | { "/include/uintrintrin.h", 22 }, 101 | { "/include/vaesintrin.h", 21 }, 102 | { "/include/vpclmulqdqintrin.h", 27 }, 103 | { "/include/waitpkgintrin.h", 24 }, 104 | { "/include/wbnoinvdintrin.h", 25 }, 105 | { "/include/wmmintrin.h", 20 }, 106 | { "/include/x86gprintrin.h", 23 }, 107 | { "/include/x86intrin.h", 20 }, 108 | { "/include/xmmintrin.h", 20 }, 109 | { "/include/xopintrin.h", 20 }, 110 | { "/include/xsavecintrin.h", 23 }, 111 | { "/include/xsaveintrin.h", 22 }, 112 | { "/include/xsaveoptintrin.h", 25 }, 113 | { "/include/xsavesintrin.h", 23 }, 114 | { "/include/xtestintrin.h", 22 }, 115 | { "/bits/atomic_base.h", 19 }, 116 | { "/atomic", 7 }, 117 | {} 118 | }; 119 | 120 | const StringMatch* s_tracySkipSubframes = s_tracySkipSubframes_; 121 | 122 | } 123 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/alloc.cpp: -------------------------------------------------------------------------------- 1 | /* alloc.c -- Memory allocation without mmap. 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "backtrace.hpp" 40 | #include "internal.hpp" 41 | 42 | #include "../common/TracyAlloc.hpp" 43 | 44 | namespace tracy 45 | { 46 | 47 | /* Allocation routines to use on systems that do not support anonymous 48 | mmap. This implementation just uses malloc, which means that the 49 | backtrace functions may not be safely invoked from a signal 50 | handler. */ 51 | 52 | /* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't 53 | report an error. */ 54 | 55 | void * 56 | backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED, 57 | size_t size, backtrace_error_callback error_callback, 58 | void *data) 59 | { 60 | void *ret; 61 | 62 | ret = tracy_malloc (size); 63 | if (ret == NULL) 64 | { 65 | if (error_callback) 66 | error_callback (data, "malloc", errno); 67 | } 68 | return ret; 69 | } 70 | 71 | /* Free memory. */ 72 | 73 | void 74 | backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED, 75 | void *p, size_t size ATTRIBUTE_UNUSED, 76 | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, 77 | void *data ATTRIBUTE_UNUSED) 78 | { 79 | tracy_free (p); 80 | } 81 | 82 | /* Grow VEC by SIZE bytes. */ 83 | 84 | void * 85 | backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED, 86 | size_t size, backtrace_error_callback error_callback, 87 | void *data, struct backtrace_vector *vec) 88 | { 89 | void *ret; 90 | 91 | if (size > vec->alc) 92 | { 93 | size_t alc; 94 | void *base; 95 | 96 | if (vec->size == 0) 97 | alc = 32 * size; 98 | else if (vec->size >= 4096) 99 | alc = vec->size + 4096; 100 | else 101 | alc = 2 * vec->size; 102 | 103 | if (alc < vec->size + size) 104 | alc = vec->size + size; 105 | 106 | base = tracy_realloc (vec->base, alc); 107 | if (base == NULL) 108 | { 109 | error_callback (data, "realloc", errno); 110 | return NULL; 111 | } 112 | 113 | vec->base = base; 114 | vec->alc = alc - vec->size; 115 | } 116 | 117 | ret = (char *) vec->base + vec->size; 118 | vec->size += size; 119 | vec->alc -= size; 120 | return ret; 121 | } 122 | 123 | /* Finish the current allocation on VEC. */ 124 | 125 | void * 126 | backtrace_vector_finish (struct backtrace_state *state, 127 | struct backtrace_vector *vec, 128 | backtrace_error_callback error_callback, 129 | void *data) 130 | { 131 | void *ret; 132 | 133 | /* With this allocator we call realloc in backtrace_vector_grow, 134 | which means we can't easily reuse the memory here. So just 135 | release it. */ 136 | if (!backtrace_vector_release (state, vec, error_callback, data)) 137 | return NULL; 138 | ret = vec->base; 139 | vec->base = NULL; 140 | vec->size = 0; 141 | vec->alc = 0; 142 | return ret; 143 | } 144 | 145 | /* Release any extra space allocated for VEC. */ 146 | 147 | int 148 | backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED, 149 | struct backtrace_vector *vec, 150 | backtrace_error_callback error_callback, 151 | void *data) 152 | { 153 | vec->alc = 0; 154 | 155 | if (vec->size == 0) 156 | { 157 | /* As of C17, realloc with size 0 is marked as an obsolescent feature, use 158 | free instead. */ 159 | tracy_free (vec->base); 160 | vec->base = NULL; 161 | return 1; 162 | } 163 | 164 | vec->base = tracy_realloc (vec->base, vec->size); 165 | if (vec->base == NULL) 166 | { 167 | error_callback (data, "realloc", errno); 168 | return 0; 169 | } 170 | 171 | return 1; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/tracy_SPSCQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 Erik Rigtorp 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include // std::enable_if, std::is_*_constructible 30 | 31 | #include "../common/TracyAlloc.hpp" 32 | 33 | #if defined (_MSC_VER) 34 | #pragma warning(push) 35 | #pragma warning(disable:4324) 36 | #endif 37 | 38 | namespace tracy { 39 | 40 | template class SPSCQueue { 41 | public: 42 | explicit SPSCQueue(const size_t capacity) 43 | : capacity_(capacity) { 44 | capacity_++; // Needs one slack element 45 | slots_ = (T*)tracy_malloc(sizeof(T) * (capacity_ + 2 * kPadding)); 46 | 47 | static_assert(alignof(SPSCQueue) == kCacheLineSize, ""); 48 | static_assert(sizeof(SPSCQueue) >= 3 * kCacheLineSize, ""); 49 | assert(reinterpret_cast(&readIdx_) - 50 | reinterpret_cast(&writeIdx_) >= 51 | static_cast(kCacheLineSize)); 52 | } 53 | 54 | ~SPSCQueue() { 55 | while (front()) { 56 | pop(); 57 | } 58 | tracy_free(slots_); 59 | } 60 | 61 | // non-copyable and non-movable 62 | SPSCQueue(const SPSCQueue &) = delete; 63 | SPSCQueue &operator=(const SPSCQueue &) = delete; 64 | 65 | template 66 | void emplace(Args &&...args) noexcept( 67 | std::is_nothrow_constructible::value) { 68 | static_assert(std::is_constructible::value, 69 | "T must be constructible with Args&&..."); 70 | auto const writeIdx = writeIdx_.load(std::memory_order_relaxed); 71 | auto nextWriteIdx = writeIdx + 1; 72 | if (nextWriteIdx == capacity_) { 73 | nextWriteIdx = 0; 74 | } 75 | while (nextWriteIdx == readIdxCache_) { 76 | readIdxCache_ = readIdx_.load(std::memory_order_acquire); 77 | } 78 | new (&slots_[writeIdx + kPadding]) T(std::forward(args)...); 79 | writeIdx_.store(nextWriteIdx, std::memory_order_release); 80 | } 81 | 82 | T *front() noexcept { 83 | auto const readIdx = readIdx_.load(std::memory_order_relaxed); 84 | if (readIdx == writeIdxCache_) { 85 | writeIdxCache_ = writeIdx_.load(std::memory_order_acquire); 86 | if (writeIdxCache_ == readIdx) { 87 | return nullptr; 88 | } 89 | } 90 | return &slots_[readIdx + kPadding]; 91 | } 92 | 93 | void pop() noexcept { 94 | static_assert(std::is_nothrow_destructible::value, 95 | "T must be nothrow destructible"); 96 | auto const readIdx = readIdx_.load(std::memory_order_relaxed); 97 | assert(writeIdx_.load(std::memory_order_acquire) != readIdx); 98 | slots_[readIdx + kPadding].~T(); 99 | auto nextReadIdx = readIdx + 1; 100 | if (nextReadIdx == capacity_) { 101 | nextReadIdx = 0; 102 | } 103 | readIdx_.store(nextReadIdx, std::memory_order_release); 104 | } 105 | 106 | size_t size() const noexcept { 107 | std::ptrdiff_t diff = writeIdx_.load(std::memory_order_acquire) - 108 | readIdx_.load(std::memory_order_acquire); 109 | if (diff < 0) { 110 | diff += capacity_; 111 | } 112 | return static_cast(diff); 113 | } 114 | 115 | bool empty() const noexcept { 116 | return writeIdx_.load(std::memory_order_acquire) == 117 | readIdx_.load(std::memory_order_acquire); 118 | } 119 | 120 | size_t capacity() const noexcept { return capacity_ - 1; } 121 | 122 | private: 123 | static constexpr size_t kCacheLineSize = 64; 124 | 125 | // Padding to avoid false sharing between slots_ and adjacent allocations 126 | static constexpr size_t kPadding = (kCacheLineSize - 1) / sizeof(T) + 1; 127 | 128 | private: 129 | size_t capacity_; 130 | T *slots_; 131 | 132 | // Align to cache line size in order to avoid false sharing 133 | // readIdxCache_ and writeIdxCache_ is used to reduce the amount of cache 134 | // coherency traffic 135 | alignas(kCacheLineSize) std::atomic writeIdx_ = {0}; 136 | alignas(kCacheLineSize) size_t readIdxCache_ = 0; 137 | alignas(kCacheLineSize) std::atomic readIdx_ = {0}; 138 | alignas(kCacheLineSize) size_t writeIdxCache_ = 0; 139 | 140 | // Padding to avoid adjacent allocations to share cache line with 141 | // writeIdxCache_ 142 | char padding_[kCacheLineSize - sizeof(SPSCQueue::writeIdxCache_)]; 143 | }; 144 | } // namespace rigtorp 145 | 146 | #if defined (_MSC_VER) 147 | #pragma warning(pop) 148 | #endif 149 | -------------------------------------------------------------------------------- /src/platform/io_sim.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const assert = std.debug.assert; 3 | 4 | const Timespec = @import("../clock.zig").Timespec; 5 | 6 | const scheduler = @import("../scheduler.zig"); 7 | const Scheduler = scheduler.SimScheduler; 8 | const CancelToken = scheduler.CancelToken; 9 | 10 | const Descriptor = i32; 11 | 12 | pub const InitError = anyerror; // TODO: narrow 13 | const SocketError = anyerror; // TODO: narrow 14 | const BindError = anyerror; // TODO: narrow 15 | const ListenError = anyerror; // TODO: narrow 16 | const SetSockOptError = anyerror; // TODO: narrow 17 | const GetSockNameError = anyerror; // TODO: narrow 18 | const PrepError = anyerror; // TODO: narrow 19 | const CompleteError = anyerror; // TODO: narrow 20 | 21 | pub const SimPlatform = struct { 22 | sched: *Scheduler, 23 | pending: usize, 24 | 25 | pub fn init(sched: *Scheduler, _: std.mem.Allocator) InitError!SimPlatform { 26 | return SimPlatform{ 27 | .sched = sched, 28 | .pending = 0, 29 | }; 30 | } 31 | 32 | pub fn deinit(_: *SimPlatform, _: std.mem.Allocator) void {} 33 | 34 | /// Polls for event completions, triggering the registered wakeup callback 35 | /// (typically to reschedule a task for continued execution). Waits up to 36 | /// `timeout' nanoseconds for a completion. Returns the number of 37 | /// completions handled. 38 | pub fn poll(_: *SimPlatform, timeout_ns: Timespec) usize { 39 | _ = timeout_ns; 40 | unreachable; // TODO: SimPlatform poll() 41 | } 42 | 43 | pub fn hasPending(platform: *SimPlatform) bool { 44 | return platform.pending != 0; 45 | } 46 | 47 | pub fn socket( 48 | _: *SimPlatform, 49 | domain: u32, 50 | socket_type: u32, 51 | protocol: u32, 52 | ) SocketError!Descriptor { 53 | _ = domain; 54 | _ = socket_type; 55 | _ = protocol; 56 | unreachable; // TODO: socket() sim 57 | } 58 | 59 | pub fn close( 60 | _: *SimPlatform, 61 | sock: Descriptor, 62 | ) void { 63 | _ = sock; 64 | unreachable; // TODO: close() sim 65 | } 66 | 67 | pub fn bind( 68 | _: *SimPlatform, 69 | sock: Descriptor, 70 | addr: *const std.os.sockaddr, 71 | len: std.os.socklen_t, 72 | ) BindError!void { 73 | _ = sock; 74 | _ = addr; 75 | _ = len; 76 | unreachable; // TODO: bind() sim 77 | } 78 | 79 | pub fn listen( 80 | _: *SimPlatform, 81 | sock: Descriptor, 82 | backlog: u31, 83 | ) ListenError!void { 84 | _ = sock; 85 | _ = backlog; 86 | unreachable; // TODO: listen() sim 87 | } 88 | 89 | pub fn setsockopt( 90 | _: *SimPlatform, 91 | fd: Descriptor, 92 | level: u32, 93 | optname: u32, 94 | opt: []const u8, 95 | ) SetSockOptError!void { 96 | _ = fd; 97 | _ = level; 98 | _ = optname; 99 | _ = opt; 100 | unreachable; // TODO: setsockopt() sim 101 | } 102 | 103 | pub fn getsockname( 104 | _: *SimPlatform, 105 | sock: Descriptor, 106 | addr: *std.os.sockaddr, 107 | addrlen: *std.os.socklen_t, 108 | ) GetSockNameError!void { 109 | _ = sock; 110 | _ = addr; 111 | _ = addrlen; 112 | unreachable; // TODO: getsockname() sim 113 | } 114 | 115 | pub fn sleep(platform: *SimPlatform, interval: Timespec) !void { 116 | var op = IoOperation{ 117 | .platform = platform, 118 | .args = .sleep, 119 | }; 120 | try platform.sched.suspendTask(interval, &op); 121 | _ = try op.complete(); 122 | } 123 | 124 | pub fn accept( 125 | platform: *SimPlatform, 126 | listen_fd: Descriptor, 127 | addr: *std.os.sockaddr, 128 | addrlen: *std.os.socklen_t, 129 | flags: u32, 130 | _: Timespec, 131 | ) !Descriptor { 132 | return IoOperation{ 133 | .platform = platform, 134 | .args = .{ .accept = .{ 135 | .listen_fd = listen_fd, 136 | .addr = addr, 137 | .addrlen = addrlen, 138 | .flags = flags, 139 | } }, 140 | }; 141 | } 142 | 143 | const IoOperation = struct { 144 | platform: *SimPlatform, 145 | 146 | args: union(enum) { 147 | sleep, 148 | accept: struct { 149 | listen_fd: Descriptor, 150 | addr: *std.os.sockaddr, 151 | addrlen: *std.os.socklen_t, 152 | flags: u32, 153 | }, 154 | connect, 155 | recv, 156 | send, 157 | }, 158 | 159 | pub fn prep( 160 | op: *IoOperation, 161 | timeout: Timespec, 162 | comptime callback: anytype, 163 | callback_ctx: anytype, 164 | callback_data: usize, 165 | ) PrepError!void { 166 | _ = op; 167 | _ = timeout; 168 | _ = callback; 169 | _ = callback_ctx; 170 | _ = callback_data; 171 | unreachable; // TODO: prep 172 | } 173 | 174 | pub fn complete(op: *IoOperation) CompleteError!usize { 175 | _ = op; 176 | unreachable; // TODO: complete 177 | } 178 | 179 | fn cancel(op: *IoOperation) void { 180 | _ = op; 181 | unreachable; // TODO: cancel 182 | } 183 | 184 | pub fn cancelToken(op: *IoOperation) CancelToken { 185 | return CancelToken.init(op, cancel); 186 | } 187 | }; 188 | }; 189 | -------------------------------------------------------------------------------- /src/list.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const assert = std.debug.assert; 3 | 4 | pub fn FwdIndexedList( 5 | comptime T: anytype, // type we're linking together 6 | comptime link_field: std.meta.FieldEnum(T), 7 | ) type { 8 | return struct { 9 | const Self = @This(); 10 | 11 | const link_info = std.meta.fieldInfo(T, link_field); 12 | const link_name = link_info.name; 13 | pub const Index = switch (@typeInfo(link_info.field_type)) { 14 | .Optional => |info| info.child, 15 | else => @compileError("expected optional type in link"), 16 | }; 17 | 18 | array: []T, 19 | head: ?Index = null, 20 | tail: ?Index = null, 21 | 22 | pub fn init(array: []T) Self { 23 | return Self{ .array = array }; 24 | } 25 | 26 | pub fn push(self: *Self, idx: Index) void { 27 | if (self.tail) |tail| { 28 | var tail_entry = &self.array[tail]; 29 | @field(tail_entry, link_name) = idx; 30 | self.tail = idx; 31 | } else { 32 | assert(self.head == null); 33 | self.head = idx; 34 | self.tail = idx; 35 | } 36 | } 37 | 38 | pub fn pop(self: *Self) ?Index { 39 | const head = self.head orelse return null; 40 | 41 | var head_entry = &self.array[head]; 42 | self.head = @field(head_entry, link_name); 43 | if (self.tail == head) { 44 | assert(self.head == null); 45 | self.tail = null; 46 | } 47 | 48 | @field(head_entry, link_name) = null; 49 | return head; 50 | } 51 | 52 | pub fn peek(self: *const Self) ?Index { 53 | return self.head; 54 | } 55 | 56 | pub fn iter(self: *const Self) Iterator(Self) { 57 | return Iterator(Self){ 58 | .list = self, 59 | .cur = self.head, 60 | }; 61 | } 62 | 63 | pub fn unlink(self: *Self, idx: Index) bool { 64 | var it = self.iter(); 65 | while (it.get()) |elem_idx| : (_ = it.next()) { 66 | if (idx == elem_idx) { 67 | _ = self.delete(&it) orelse unreachable; 68 | return true; 69 | } 70 | } 71 | return false; 72 | } 73 | 74 | // TODO: comment - NOTE that delete advances the iterator 75 | // so beware of using this in a while loop 76 | pub fn delete(self: *Self, it: *Iterator(Self)) ?Index { 77 | const cur = it.cur orelse return null; 78 | var cur_entry = &self.array[cur]; 79 | const next = @field(cur_entry, link_name); 80 | @field(cur_entry, link_name) = null; 81 | 82 | if (it.prev) |prev| { 83 | @field(self.array[prev], link_name) = next; 84 | } else { 85 | self.head = next; 86 | } 87 | 88 | if (next == null) self.tail = it.prev; 89 | 90 | it.cur = next; 91 | 92 | return cur; 93 | } 94 | }; 95 | } 96 | 97 | fn Iterator(comptime List: anytype) type { 98 | return struct { 99 | const Self = @This(); 100 | 101 | list: *const List, 102 | 103 | prev: ?List.Index = null, 104 | cur: ?List.Index, 105 | 106 | pub fn get(self: *Self) ?List.Index { 107 | return self.cur; 108 | } 109 | 110 | pub fn next(self: *Self) ?List.Index { 111 | const cur_idx = self.cur orelse return null; 112 | const cur_entry = self.list.array[cur_idx]; 113 | self.prev = self.cur; 114 | 115 | const fwd = @field(cur_entry, List.link_name); 116 | self.cur = fwd; 117 | return self.cur; 118 | } 119 | }; 120 | } 121 | 122 | test "basic" { 123 | const Test = struct { 124 | x: u32, 125 | next: ?usize = null, 126 | }; 127 | 128 | const TestList = FwdIndexedList(Test, .next); 129 | try std.testing.expect(TestList.Index == usize); 130 | 131 | var arr = [2]Test{ 132 | .{ .x = 1 }, 133 | .{ .x = 2 }, 134 | }; 135 | var l = TestList.init(&arr); 136 | 137 | l.push(0); 138 | try std.testing.expectEqual(@as(u32, 1), arr[l.peek().?].x); 139 | 140 | l.push(1); 141 | try std.testing.expectEqual(@as(u32, 1), arr[l.peek().?].x); 142 | 143 | try std.testing.expectEqual(@as(u32, 1), arr[l.pop().?].x); 144 | try std.testing.expectEqual(@as(u32, 2), arr[l.peek().?].x); 145 | try std.testing.expectEqual(@as(u32, 2), arr[l.pop().?].x); 146 | try std.testing.expect(l.pop() == null); 147 | 148 | l.push(0); 149 | l.push(1); 150 | 151 | { 152 | var it = l.iter(); 153 | var count: usize = 0; 154 | while (it.get()) |elem| : (_ = it.next()) { 155 | try std.testing.expect(elem == count); 156 | count += 1; 157 | } 158 | try std.testing.expect(it.get() == null); 159 | } 160 | 161 | { 162 | var it = l.iter(); 163 | try std.testing.expect(l.delete(&it).? == 0); 164 | try std.testing.expect(it.get().? == 1); 165 | try std.testing.expect(l.delete(&it).? == 1); 166 | try std.testing.expect(it.get() == null); 167 | try std.testing.expect(l.head == null); 168 | try std.testing.expect(l.tail == null); 169 | try std.testing.expect(l.delete(&it) == null); 170 | } 171 | 172 | l.push(0); 173 | l.push(1); 174 | 175 | { 176 | var it = l.iter(); 177 | try std.testing.expect(it.get().? == 0); 178 | try std.testing.expect(it.next().? == 1); 179 | try std.testing.expect(l.delete(&it).? == 1); 180 | try std.testing.expect(it.get() == null); 181 | try std.testing.expect(l.head.? == l.tail.?); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/event.zig: -------------------------------------------------------------------------------- 1 | //! Tools for creating and emitting event records, for logging and other 2 | //! observability means. 3 | const std = @import("std"); 4 | const assert = std.debug.assert; 5 | pub const Level = std.log.Level; 6 | 7 | const ztracy = @import("ztracy"); 8 | 9 | const Timespec = @import("clock.zig").Timespec; 10 | 11 | pub const EventWriter = std.fs.File.Writer; 12 | 13 | pub const SyncEventWriter = struct { 14 | writer: EventWriter, 15 | mutex: *std.Thread.Mutex, 16 | }; 17 | 18 | // The Emitter that will be used by various threads to emit registered events 19 | // of interest. 20 | pub fn Emitter(comptime Clock: type) type { 21 | return struct { 22 | const Self = @This(); 23 | 24 | log_level: Level, 25 | sync_writer: SyncEventWriter, 26 | 27 | pub fn init( 28 | log_level: Level, 29 | sync_writer: SyncEventWriter, 30 | ) Self { 31 | return Self{ 32 | .log_level = log_level, 33 | .sync_writer = sync_writer, 34 | }; 35 | } 36 | 37 | pub fn emit( 38 | self: *Self, 39 | clock: *Clock, 40 | tid: usize, 41 | comptime Event: type, 42 | user: Event.User, 43 | ) void { 44 | // bail if this is at a higher verbosity 45 | if (@enumToInt(Event.level) > @enumToInt(self.log_level)) return; 46 | 47 | const ev = Event.init(clock.now(), user); 48 | 49 | var buf: [4096]u8 = undefined; 50 | var bw = std.io.fixedBufferStream(&buf); 51 | 52 | bw.writer().print("{d:>15} [{d:0>3}] {s:<9} {s:12} {s:16} ", .{ 53 | ev.timestamp, 54 | tid, 55 | "(" ++ Event.level.asText() ++ ")", 56 | Event.namespace, 57 | @tagName(Event.code), 58 | }) catch @panic("Unable to write event"); 59 | 60 | if (std.meta.fields(Event.User).len > 0) { 61 | bw.writer().writeAll("- ") catch @panic("Unable to write event"); 62 | } 63 | 64 | inline for (std.meta.fields(Event.User)) |f| { 65 | bw.writer().print("{s}={any} ", .{ 66 | f.name, 67 | @field(user, f.name), 68 | }) catch @panic("Unable to write event"); 69 | } 70 | 71 | bw.writer().writeAll("\n") catch @panic("Unable to write event"); 72 | 73 | // Emit to tracy - a nop unless built with -Denable-tracy=true 74 | ztracy.Message(bw.getWritten()); 75 | 76 | // Emit into sync_writer stream 77 | { 78 | self.sync_writer.mutex.lock(); 79 | defer self.sync_writer.mutex.unlock(); 80 | 81 | self.sync_writer.writer.writeAll(bw.getWritten()) catch 82 | @panic("Unable to write to event stream"); 83 | } 84 | } 85 | }; 86 | } 87 | 88 | /// Returns a namespace-scoped registry of events. Each event has a 89 | /// corresponding code in the `Tag' enum, which must be registered via the 90 | /// register() method. 91 | pub fn EventRegistry(comptime namespace: []const u8, comptime Tag: type) type { 92 | assert(@typeInfo(Tag) == .Enum); 93 | 94 | return struct { 95 | const Self = @This(); 96 | 97 | /// Registers an event in this scoped registry. Each event type has a 98 | /// corresponding level (following std.log.Level) and a user-defined 99 | /// struct of fields for this event. 100 | pub fn register( 101 | comptime code: Tag, 102 | comptime level: Level, 103 | comptime User: type, 104 | ) type { 105 | assert(@typeInfo(User) == .Struct); 106 | 107 | return struct { 108 | const code = code; 109 | const namespace = namespace; 110 | const level = level; 111 | pub const User = User; 112 | 113 | timestamp: Timespec, 114 | user: User, 115 | 116 | /// Constructs an event record at the given timestamp and with 117 | /// the corresponding `user' data. 118 | pub fn init(ts: Timespec, user: User) @This() { 119 | return @This(){ .timestamp = ts, .user = user }; 120 | } 121 | }; 122 | } 123 | }; 124 | } 125 | 126 | test "event registry" { 127 | const alloc = std.testing.allocator; 128 | const SimClock = @import("clock.zig").SimClock; 129 | 130 | const MyScopedRegistry = EventRegistry("my.namespace", enum { 131 | foo, 132 | }); 133 | 134 | const FooEvent = MyScopedRegistry.register(.foo, .info, struct { 135 | value1: u64, 136 | }); 137 | 138 | try std.testing.expectEqualStrings("info", FooEvent.level.asText()); 139 | try std.testing.expectEqualStrings("my.namespace", FooEvent.namespace); 140 | try std.testing.expectEqualStrings("foo", @tagName(FooEvent.code)); 141 | 142 | // create a temporary directory for our log output 143 | var tmp_dir = std.testing.tmpDir(.{}); 144 | defer tmp_dir.cleanup(); 145 | 146 | const tmp_file = try tmp_dir.dir.createFile("stderr.log", .{ .read = true }); 147 | defer tmp_file.close(); 148 | 149 | var tmp_mutex = std.Thread.Mutex{}; 150 | var sync_writer = SyncEventWriter{ 151 | .writer = tmp_file.writer(), 152 | .mutex = &tmp_mutex, 153 | }; 154 | 155 | var clk = SimClock{}; 156 | clk.setTime(1000); 157 | 158 | var e = Emitter(SimClock).init(.info, sync_writer); 159 | e.emit(&clk, 0, FooEvent, .{ .value1 = 42 }); 160 | 161 | const expect = " 1000 [000] (info) my.namespace foo - value1=42 \n"; 162 | 163 | try tmp_file.seekTo(0); 164 | const buf = try tmp_file.reader().readAllAlloc(alloc, expect.len); 165 | defer alloc.free(buf); 166 | 167 | try std.testing.expectEqualStrings(expect, buf); 168 | } 169 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/client/TracyScoped.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __TRACYSCOPED_HPP__ 2 | #define __TRACYSCOPED_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../common/TracySystem.hpp" 9 | #include "../common/TracyAlign.hpp" 10 | #include "../common/TracyAlloc.hpp" 11 | #include "TracyProfiler.hpp" 12 | 13 | namespace tracy 14 | { 15 | 16 | class ScopedZone 17 | { 18 | public: 19 | ScopedZone( const ScopedZone& ) = delete; 20 | ScopedZone( ScopedZone&& ) = delete; 21 | ScopedZone& operator=( const ScopedZone& ) = delete; 22 | ScopedZone& operator=( ScopedZone&& ) = delete; 23 | 24 | tracy_force_inline ScopedZone( const SourceLocationData* srcloc, bool is_active = true ) 25 | #ifdef TRACY_ON_DEMAND 26 | : m_active( is_active && GetProfiler().IsConnected() ) 27 | #else 28 | : m_active( is_active ) 29 | #endif 30 | { 31 | if( !m_active ) return; 32 | #ifdef TRACY_ON_DEMAND 33 | m_connectionId = GetProfiler().ConnectionId(); 34 | #endif 35 | TracyQueuePrepare( QueueType::ZoneBegin ); 36 | MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); 37 | MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); 38 | TracyQueueCommit( zoneBeginThread ); 39 | } 40 | 41 | tracy_force_inline ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active = true ) 42 | #ifdef TRACY_ON_DEMAND 43 | : m_active( is_active && GetProfiler().IsConnected() ) 44 | #else 45 | : m_active( is_active ) 46 | #endif 47 | { 48 | if( !m_active ) return; 49 | #ifdef TRACY_ON_DEMAND 50 | m_connectionId = GetProfiler().ConnectionId(); 51 | #endif 52 | GetProfiler().SendCallstack( depth ); 53 | 54 | TracyQueuePrepare( QueueType::ZoneBeginCallstack ); 55 | MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); 56 | MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); 57 | TracyQueueCommit( zoneBeginThread ); 58 | } 59 | 60 | tracy_force_inline ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active = true ) 61 | #ifdef TRACY_ON_DEMAND 62 | : m_active( is_active && GetProfiler().IsConnected() ) 63 | #else 64 | : m_active( is_active ) 65 | #endif 66 | { 67 | if( !m_active ) return; 68 | #ifdef TRACY_ON_DEMAND 69 | m_connectionId = GetProfiler().ConnectionId(); 70 | #endif 71 | TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLoc ); 72 | const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz ); 73 | MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); 74 | MemWrite( &item->zoneBegin.srcloc, srcloc ); 75 | TracyQueueCommit( zoneBeginThread ); 76 | } 77 | 78 | tracy_force_inline ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active = true ) 79 | #ifdef TRACY_ON_DEMAND 80 | : m_active( is_active && GetProfiler().IsConnected() ) 81 | #else 82 | : m_active( is_active ) 83 | #endif 84 | { 85 | if( !m_active ) return; 86 | #ifdef TRACY_ON_DEMAND 87 | m_connectionId = GetProfiler().ConnectionId(); 88 | #endif 89 | GetProfiler().SendCallstack( depth ); 90 | 91 | TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLocCallstack ); 92 | const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz ); 93 | MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); 94 | MemWrite( &item->zoneBegin.srcloc, srcloc ); 95 | TracyQueueCommit( zoneBeginThread ); 96 | } 97 | 98 | tracy_force_inline ~ScopedZone() 99 | { 100 | if( !m_active ) return; 101 | #ifdef TRACY_ON_DEMAND 102 | if( GetProfiler().ConnectionId() != m_connectionId ) return; 103 | #endif 104 | TracyQueuePrepare( QueueType::ZoneEnd ); 105 | MemWrite( &item->zoneEnd.time, Profiler::GetTime() ); 106 | TracyQueueCommit( zoneEndThread ); 107 | } 108 | 109 | tracy_force_inline void Text( const char* txt, size_t size ) 110 | { 111 | assert( size < std::numeric_limits::max() ); 112 | if( !m_active ) return; 113 | #ifdef TRACY_ON_DEMAND 114 | if( GetProfiler().ConnectionId() != m_connectionId ) return; 115 | #endif 116 | auto ptr = (char*)tracy_malloc( size ); 117 | memcpy( ptr, txt, size ); 118 | TracyQueuePrepare( QueueType::ZoneText ); 119 | MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); 120 | MemWrite( &item->zoneTextFat.size, (uint16_t)size ); 121 | TracyQueueCommit( zoneTextFatThread ); 122 | } 123 | 124 | tracy_force_inline void Name( const char* txt, size_t size ) 125 | { 126 | assert( size < std::numeric_limits::max() ); 127 | if( !m_active ) return; 128 | #ifdef TRACY_ON_DEMAND 129 | if( GetProfiler().ConnectionId() != m_connectionId ) return; 130 | #endif 131 | auto ptr = (char*)tracy_malloc( size ); 132 | memcpy( ptr, txt, size ); 133 | TracyQueuePrepare( QueueType::ZoneName ); 134 | MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); 135 | MemWrite( &item->zoneTextFat.size, (uint16_t)size ); 136 | TracyQueueCommit( zoneTextFatThread ); 137 | } 138 | 139 | tracy_force_inline void Color( uint32_t color ) 140 | { 141 | if( !m_active ) return; 142 | #ifdef TRACY_ON_DEMAND 143 | if( GetProfiler().ConnectionId() != m_connectionId ) return; 144 | #endif 145 | TracyQueuePrepare( QueueType::ZoneColor ); 146 | MemWrite( &item->zoneColor.r, uint8_t( ( color ) & 0xFF ) ); 147 | MemWrite( &item->zoneColor.g, uint8_t( ( color >> 8 ) & 0xFF ) ); 148 | MemWrite( &item->zoneColor.b, uint8_t( ( color >> 16 ) & 0xFF ) ); 149 | TracyQueueCommit( zoneColorThread ); 150 | } 151 | 152 | tracy_force_inline void Value( uint64_t value ) 153 | { 154 | if( !m_active ) return; 155 | #ifdef TRACY_ON_DEMAND 156 | if( GetProfiler().ConnectionId() != m_connectionId ) return; 157 | #endif 158 | TracyQueuePrepare( QueueType::ZoneValue ); 159 | MemWrite( &item->zoneValue.value, value ); 160 | TracyQueueCommit( zoneValueThread ); 161 | } 162 | 163 | tracy_force_inline bool IsActive() const { return m_active; } 164 | 165 | private: 166 | const bool m_active; 167 | 168 | #ifdef TRACY_ON_DEMAND 169 | uint64_t m_connectionId; 170 | #endif 171 | }; 172 | 173 | } 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vortex 2 | 3 | Vortex is a Zig library for [structured 4 | concurrency](https://en.wikipedia.org/wiki/Structured_concurrency) and 5 | asynchronous event processing. It builds on Zig's language support for async 6 | functions (suspend/resume and async/await), providing the user with ergonomic 7 | task spawning, joining, cancellation, and timeouts. An included I/O engine 8 | supports io_uring and epoll (on Linux) and kqueue (on MacOS/Darwin), with 9 | support for IOCP (Windows) planned. 10 | 11 | :warning: **Disclaimer:** Vortex is a hobby project. Expect nothing to work and 12 | everything to change. 13 | 14 | ## Getting started 15 | 16 | Assumes you have a recent nightly build of `zig` in your path and are running 17 | on a recent Linux or MacOS. Support for other platforms discussed below. If 18 | you're new to Zig, see the [Zig getting started 19 | page](https://ziglang.org/learn/getting-started/). 20 | 21 | Run `zig build test` to run a series of unit tests. Among these unit tests, 22 | those in `tests/` are against the public API and make for good examples. 23 | 24 | Run `zig build list-demos` to see a list of complete demos that use the public 25 | API exclusively. Each can be built by `zig build `. 26 | 27 | ## A TCP Echo Server ("Hello, World") 28 | 29 | This code shows the core of a simple TCP echo server in Vortex. The [echo 30 | demo](demos/echo.zig) expands on this and includes a concurrent client and 31 | server pair. 32 | 33 | ```zig 34 | fn session(stream: *vx.TcpStream) !void { 35 | var buf: [128]u8 = undefined; 36 | while (true) { 37 | // await a message from the client 38 | const rc = try stream.recv(&buf, null); 39 | if (rc == 0) break; // client disconnected 40 | 41 | // send back what we received 42 | _ = try stream.send(buf[0..rc], null); 43 | } 44 | } 45 | 46 | fn start(addr: std.net.Address) !void { 47 | var l = try vx.net.startTcpListener(addr, 1); 48 | defer l.deinit(); 49 | 50 | while (true) { 51 | // accept a connection on the listener 52 | var stream = try l.accept(null); 53 | defer stream.close(); 54 | 55 | // spawn a new task for the session 56 | var task: vx.task.SpawnHandle(session) = undefined; 57 | try vx.task.spawn(&task, .{&stream}, null); 58 | 59 | // Wait for this session to finish. In this simple example, 60 | // we only allow one client to connect at a time. 61 | try task.join(); 62 | } 63 | } 64 | 65 | const ip4: []const u8 = "0.0.0.0"; 66 | const port: u16 = 8888; 67 | const addr = try std.net.Address.parseIp4(ip4, port); 68 | try vx.run(start, .{addr}); 69 | ``` 70 | 71 | ## Design Goals and Assumptions 72 | 73 | - **Zero heap allocations past startup.** All structure sizes (e.g., maximum 74 | number of tasks) are known at startup and pre-allocated. Tasks (user code) can 75 | still perform allocations, but the runtime does not. This implies task spawning 76 | can fail, and user code must be prepared to handle the TooManyTasks error. 77 | 78 | - **Pluggable, cross-platform I/O engines.** The core runtime is designed around 79 | a proactor-style I/O system, where operations are submitted, completed, and then 80 | the runtime is notified of the completion. Reactor-style APIs (epoll, kqueue, 81 | etc.) are supported through an adapter. 82 | 83 | - **Multi-threaded task execution.** The runtime launches a configurable number 84 | of OS threads to run tasks. As of this writing, Vortex has a 85 | resource-**inefficient** scheduler with a global queue for synchronization. 86 | Once we have benchmarks representing something useful (e.g., a web server or 87 | similar) then [advanced 88 | optimizations](https://zig.news/kprotty/resource-efficient-thread-pools-with-zig-3291) 89 | (work-stealing, wake throttling, etc.) will be explored. 90 | 91 | ## Basic feature roadmap 92 | 93 | - [x] Task model 94 | - [x] Spawning and joining 95 | - [x] Cancellation 96 | - [x] select API to choose among several tasks 97 | - [x] I/O Engine 98 | - [x] kqueue (Darwin) 99 | - [x] epoll (Linux) 100 | - [x] io_uring (Linux) 101 | - [ ] Simulation 102 | - [ ] IOCP (Windows) 103 | - [x] Timeouts 104 | - [x] I/O timeouts 105 | - [x] Task-level timeouts 106 | - [x] Deterministic clock & autojump testing 107 | - [ ] Scheduler optimization 108 | - [x] Basic multi-threaded runtime 109 | - [ ] Idle thread efficiency (wait/notify) 110 | - [ ] Work stealing 111 | - [ ] Synchronization 112 | - [x] Task-aware futex 113 | - [x] Barriers 114 | - [x] Channels 115 | - [ ] ResetEvent 116 | - [ ] Mutex, RWLock 117 | - [ ] Demos and benchmarks 118 | - [x] TCP echo server 119 | - [x] Cancellation torture tests & fuzzing 120 | - [ ] HTTP server benchmark 121 | - [ ] Observability 122 | - [x] Event logging 123 | - [ ] Metrics collection 124 | - [ ] Scheduler tracing 125 | - [ ] Safety and Invariant checking 126 | - [ ] Orphaned tasks 127 | - [ ] CPU-hogging tasks 128 | - [ ] Networking APIs 129 | - [x] Primitive tcp support (ipv4 only, primitive send/recv) 130 | - [ ] Support ipv6 131 | - [ ] Support udp 132 | - [ ] File APIs 133 | - [ ] posix by default, backed by thread pool 134 | - [ ] io_uring where possible 135 | - [x] Signals 136 | 137 | ## Longer term 138 | 139 | Just some rough notes for now: 140 | 141 | - **Priorities and resource accounting.** In large systems, we'll likely want to 142 | track, schedule, and budget different tasks and task groups. Consider a system 143 | with foreground tasks that handle user requests, and background tasks that 144 | perform system maintenance or other cleanup. We may want to prioritize 145 | foreground tasks, until some resources run low and we need to prioritize 146 | background tasks. How should a user express these priorities, and how will they 147 | measure actual resource consumption to tune various policies? 148 | 149 | - **Simulated I/O.** To facilitate deterministic testing and fault injection, 150 | we'll want implementations of the I/O APIs that mock the OS kernel abstractions 151 | with in-memory models. 152 | 153 | - **Compound I/O engines.** Supporting RDMA or kernel-bypass networking (with 154 | DPDK or similar) requires an I/O engine that can use the accelerated access 155 | methods where possible, and fall back to conventional operations where needed. 156 | An I/O engine now has to juggle multiple polling APIs to reap completions and 157 | readiness notifications across different backends. 158 | 159 | - **Advanced I/O offloads.** Recent versions of io_uring add support for 160 | *automatic buffer selection*, enabling the kernel to allocate buffers at the 161 | time an operation needs it, rather than at the time of submission. We've 162 | structured the APIs for the I/O engine to support this and other forms of 163 | advanced I/O offload, but some work will be required to integrate them. 164 | -------------------------------------------------------------------------------- /src/network.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const Timespec = @import("clock.zig").Timespec; 5 | const max_time = @import("clock.zig").max_time; 6 | const RuntimeImpl = @import("runtime.zig").RuntimeImpl; 7 | 8 | // Portions of this code are adapted from zig-network, via MIT license: 9 | // https://github.com/MasterQ32/zig-network (commit 6f1563a) 10 | 11 | const is_linux = builtin.os.tag == .linux; 12 | const is_windows = builtin.os.tag == .windows; 13 | const is_darwin = builtin.os.tag.isDarwin(); 14 | const is_freebsd = builtin.os.tag == .freebsd; 15 | const is_openbsd = builtin.os.tag == .openbsd; 16 | const is_netbsd = builtin.os.tag == .netbsd; 17 | const is_dragonfly = builtin.os.tag == .dragonfly; 18 | 19 | // use these to test collections of OS type 20 | const is_bsd = is_darwin or is_freebsd or is_openbsd or is_netbsd or is_dragonfly; 21 | 22 | pub fn Impl(comptime R: type) type { 23 | return struct { 24 | const Self = @This(); 25 | const Runtime = R; 26 | const Platform = R.Platform; 27 | 28 | pub const Listener = struct { 29 | rt: *Runtime, 30 | addr: std.net.Address, 31 | fd: Platform.Descriptor, 32 | 33 | pub const InitError = anyerror; // TODO: narrow 34 | 35 | pub fn init( 36 | rt: *Runtime, 37 | addr: std.net.Address, 38 | backlog: u31, 39 | ) InitError!Listener { 40 | // create listener socket at endpoint 41 | const fd = try rt.io().socket( 42 | addr.any.family, 43 | std.os.SOCK.STREAM | std.os.SOCK.CLOEXEC, 44 | 0, 45 | ); 46 | 47 | // Enable port reuse 48 | var opt: c_int = 1; 49 | try rt.io().setsockopt( 50 | fd, 51 | std.os.SOL.SOCKET, 52 | std.os.SO.REUSEADDR, 53 | std.mem.asBytes(&opt), 54 | ); 55 | 56 | // Bind 57 | try rt.io().bind( 58 | fd, 59 | &addr.any, 60 | addr.getOsSockLen(), 61 | ); 62 | 63 | // Listen 64 | try rt.io().listen(fd, backlog); 65 | 66 | return Listener{ 67 | .rt = rt, 68 | .addr = addr, 69 | .fd = fd, 70 | }; 71 | } 72 | 73 | pub fn close(self: *Listener) void { 74 | self.rt.io().close(self.fd); 75 | } 76 | 77 | pub fn accept( 78 | self: *Listener, 79 | timeout_ns: ?Timespec, 80 | ) !Stream { 81 | var peer: std.net.Address = undefined; 82 | 83 | const timeout = timeout_ns orelse max_time; 84 | var asize: std.os.socklen_t = @sizeOf(@TypeOf(peer.any)); 85 | 86 | const fd = try self.rt.io().accept( 87 | self.fd, 88 | &peer.any, 89 | &asize, 90 | std.os.SOCK.CLOEXEC, // flags - others may be added by loop 91 | timeout, 92 | ); 93 | 94 | // on bsd (incl darwin) set the NOSIGNAL once 95 | if (is_bsd) { 96 | // set the options to ON 97 | const value: c_int = 1; 98 | try self.rt.io().setsockopt( 99 | fd, 100 | std.os.SOL.SOCKET, 101 | std.os.SO.NOSIGPIPE, 102 | std.mem.asBytes(&value), 103 | ); 104 | } 105 | 106 | return Stream{ 107 | .rt = self.rt, 108 | .fd = fd, 109 | .peer = peer, 110 | }; 111 | } 112 | }; 113 | 114 | pub const Stream = struct { 115 | rt: *Runtime, 116 | fd: Platform.Descriptor, 117 | peer: std.net.Address, 118 | 119 | pub const ConnectError = anyerror; // TODO: narrow 120 | 121 | pub fn connect( 122 | rt: *Runtime, 123 | target: std.net.Address, 124 | timeout_ns: ?Timespec, 125 | ) ConnectError!Stream { 126 | const fd = try rt.io().socket( 127 | target.any.family, 128 | std.os.SOCK.STREAM | std.os.SOCK.CLOEXEC, 129 | 0, 130 | ); 131 | 132 | // on bsd (incl darwin) set the NOSIGNAL once 133 | if (is_bsd) { 134 | // set the options to ON 135 | const value: c_int = 1; 136 | try rt.io().setsockopt( 137 | fd, 138 | std.os.SOL.SOCKET, 139 | std.os.SO.NOSIGPIPE, 140 | std.mem.asBytes(&value), 141 | ); 142 | } 143 | 144 | const timeout = timeout_ns orelse max_time; 145 | var endpoint = target; 146 | _ = try rt.io().connect( 147 | fd, 148 | &endpoint.any, 149 | endpoint.getOsSockLen(), 150 | timeout, 151 | ); 152 | 153 | return Stream{ 154 | .rt = rt, 155 | .fd = fd, 156 | .peer = endpoint, 157 | }; 158 | } 159 | 160 | pub fn close(self: *Stream) void { 161 | self.rt.io().close(self.fd); 162 | } 163 | 164 | pub fn recv( 165 | self: Stream, 166 | buf: []u8, 167 | timeout_ns: ?Timespec, 168 | ) !usize { 169 | const timeout = timeout_ns orelse max_time; 170 | const flags = if (is_windows or is_bsd) 171 | 0 172 | else 173 | std.os.linux.MSG.NOSIGNAL; 174 | return self.rt.io().recv(self.fd, buf, flags, timeout); 175 | } 176 | 177 | pub fn send( 178 | self: Stream, 179 | buf: []const u8, 180 | timeout_ns: ?Timespec, 181 | ) !usize { 182 | const timeout = timeout_ns orelse max_time; 183 | const flags = if (is_windows or is_bsd) 184 | 0 185 | else 186 | std.os.linux.MSG.NOSIGNAL; 187 | return self.rt.io().send(self.fd, buf, flags, timeout); 188 | } 189 | }; 190 | 191 | pub const testing = struct { 192 | pub fn unusedTcpPort(rt: *Runtime) !std.net.Address { 193 | // Strategy adapted from pytest-asyncio: 194 | // - create a tcp socket 195 | // - bind to 0.0.0.0 with port "0" 196 | // - ask OS what port we received via getsockname 197 | const fd = try rt.io().socket( 198 | std.os.AF.INET, 199 | std.os.SOCK.STREAM | std.os.SOCK.CLOEXEC, 200 | 0, 201 | ); 202 | defer rt.io().close(fd); 203 | 204 | var addr = std.net.Address.initIp4(.{ 0, 0, 0, 0 }, 0); 205 | try rt.io().bind(fd, &addr.any, addr.getOsSockLen()); 206 | 207 | var size: std.os.socklen_t = @sizeOf(std.os.sockaddr); 208 | 209 | try rt.io().getsockname(fd, &addr.any, &size); 210 | 211 | return addr; 212 | } 213 | }; 214 | }; 215 | } 216 | -------------------------------------------------------------------------------- /src/runtime.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const assert = std.debug.assert; 3 | const Atomic = std.atomic.Atomic; 4 | 5 | const platform = @import("platform.zig"); 6 | const scheduler = @import("scheduler.zig"); 7 | const EventRegistry = @import("event.zig").EventRegistry; 8 | const SignalJunction = @import("signal.zig").SignalJunction; 9 | 10 | const ztracy = @import("ztracy"); 11 | 12 | threadlocal var thread_id: usize = 0; 13 | 14 | pub fn threadId() usize { 15 | return thread_id; 16 | } 17 | 18 | pub const SimRuntime = RuntimeImpl(platform.SimPlatform); 19 | pub const DefaultRuntime = RuntimeImpl(platform.DefaultPlatform); 20 | 21 | pub fn RuntimeImpl(comptime P: type) type { 22 | return struct { 23 | const Runtime = @This(); 24 | 25 | pub const Platform = P; 26 | pub const Scheduler = Platform.Scheduler; 27 | pub const Clock = Scheduler.Clock; 28 | pub const Config = @import("config.zig").Config(Runtime); 29 | pub const Emitter = @import("event.zig").Emitter(Clock); 30 | 31 | emitter: *Emitter, 32 | clock: *Clock, 33 | sched: *Scheduler, 34 | threads: []WorkerThread, 35 | signal_junction: *SignalJunction, 36 | 37 | pub fn init(alloc: std.mem.Allocator, config: Config) !Runtime { 38 | var emitter = try alloc.create(Emitter); 39 | emitter.* = Emitter.init(config.log_level, config.event_writer); 40 | 41 | var clk = try alloc.create(Clock); 42 | clk.* = Clock{}; 43 | 44 | var sched = try alloc.create(Scheduler); 45 | sched.* = try Scheduler.init(alloc, clk, emitter, config.scheduler); 46 | 47 | var threads = try alloc.alloc(WorkerThread, config.task_threads); 48 | for (threads) |*thread| { 49 | thread.* = try WorkerThread.init( 50 | alloc, 51 | config.platform, 52 | sched, 53 | emitter, 54 | ); 55 | } 56 | 57 | var signal_junction = try alloc.create(SignalJunction); 58 | try SignalJunction.init(signal_junction); 59 | 60 | return Runtime{ 61 | .emitter = emitter, 62 | .clock = clk, 63 | .sched = sched, 64 | .threads = threads, 65 | .signal_junction = signal_junction, 66 | }; 67 | } 68 | 69 | pub fn deinit(self: *Runtime, alloc: std.mem.Allocator) void { 70 | self.signal_junction.deinit(); 71 | for (self.threads) |*thread| { 72 | thread.deinit(alloc); 73 | } 74 | self.sched.deinit(alloc); 75 | 76 | alloc.destroy(self.signal_junction); 77 | alloc.free(self.threads); 78 | alloc.destroy(self.sched); 79 | alloc.destroy(self.clock); 80 | alloc.destroy(self.emitter); 81 | } 82 | 83 | pub fn run( 84 | self: *Runtime, 85 | comptime entry: anytype, 86 | args: anytype, 87 | ) anyerror!void { 88 | const Args = @TypeOf(args); 89 | 90 | const wrap = struct { 91 | fn inner( 92 | sched: *Scheduler, 93 | a: Args, 94 | done: *Atomic(bool), 95 | ) !void { 96 | // Immediately suspend the frame, returning control so we 97 | // can start the scheduler and event loop. 98 | suspend { 99 | _ = sched.spawnInit(@frame()); 100 | } 101 | 102 | defer { 103 | assert(sched.runqueue.empty()); // no runnable tasks 104 | 105 | done.store(true, .Monotonic); 106 | sched.joinInit(); 107 | } 108 | 109 | // At this point the 'init' task is running, so we can 110 | // execute the entry method given to us. 111 | return @call(.{}, entry, a); 112 | } 113 | }.inner; 114 | 115 | var done = Atomic(bool).init(false); 116 | 117 | // start wrapper, which immediately suspends & reschedules itself 118 | var frame = &async wrap(self.sched, args, &done); 119 | 120 | // start worker threads 121 | for (self.threads[1..]) |*w, i| { 122 | assert(w.handle == null); 123 | w.handle = try std.Thread.spawn( 124 | .{}, 125 | WorkerThread.start, 126 | .{ w, self, i + 1, &done }, 127 | ); 128 | } 129 | 130 | // And run the 0th worker in place 131 | self.threads[0].start(self, 0, &done); 132 | 133 | for (self.threads[1..]) |*w| { 134 | w.handle.?.join(); 135 | } 136 | 137 | // async hygiene: match the async call above, although we know that 138 | // the function has returned by this point 139 | try nosuspend await frame; 140 | 141 | // Invariant checks 142 | assert(self.sched.runqueue.empty()); // no runnable tasks 143 | } 144 | 145 | pub fn io(self: *Runtime) *Platform { 146 | return &self.threads[threadId()].loop; 147 | } 148 | 149 | const WorkerThread = struct { 150 | loop: Platform, 151 | handle: ?std.Thread = null, 152 | 153 | fn init( 154 | alloc: std.mem.Allocator, 155 | config: Platform.Config, 156 | sched: *Scheduler, 157 | emitter: *Emitter, 158 | ) !WorkerThread { 159 | return WorkerThread{ 160 | .loop = try Platform.init(alloc, config, sched, emitter), 161 | }; 162 | } 163 | 164 | fn deinit(self: *WorkerThread, alloc: std.mem.Allocator) void { 165 | self.loop.deinit(alloc); 166 | } 167 | 168 | fn start( 169 | self: *WorkerThread, 170 | rt: *Runtime, 171 | id: usize, 172 | done: *Atomic(bool), 173 | ) void { 174 | // setup TLS 175 | thread_id = id; 176 | 177 | rt.emitter.emit(rt.clock, id, ThreadStartEvent, .{}); 178 | 179 | // The core loop 180 | while (!done.load(.Monotonic)) { 181 | // Check for I/O activity and timeouts 182 | { 183 | const tracy_zone = ztracy.ZoneNC(@src(), "I/O poll", 0x00_ff_00_00); 184 | defer tracy_zone.End(); 185 | 186 | // TODO: poll timeout as constant 187 | _ = self.loop.poll(std.time.ns_per_ms) catch |err| { 188 | std.debug.panic("Runtime error: {}\n", .{err}); 189 | }; 190 | } 191 | 192 | // Drive task execution in the scheduler 193 | { 194 | const tracy_zone = ztracy.ZoneNC(@src(), "Task exec", 0x00_00_00_ff); 195 | defer tracy_zone.End(); 196 | 197 | _ = rt.sched.tick(); 198 | } 199 | } 200 | 201 | // Invariant checks 202 | assert(!self.loop.hasPending()); // no pending I/O operations 203 | 204 | rt.emitter.emit(rt.clock, id, ThreadEndEvent, .{}); 205 | } 206 | }; 207 | }; 208 | } 209 | 210 | const ScopedRegistry = EventRegistry("vx.runtime", enum { 211 | thread_start, 212 | thread_end, 213 | }); 214 | 215 | const ThreadStartEvent = ScopedRegistry.register(.thread_start, .debug, struct {}); 216 | const ThreadEndEvent = ScopedRegistry.register(.thread_end, .debug, struct {}); 217 | -------------------------------------------------------------------------------- /tests/cancel.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const assert = std.debug.assert; 4 | 5 | const vortex = if (builtin.is_test) 6 | @import("../vortex.zig") 7 | else 8 | @import("vortex"); 9 | 10 | const alloc = std.testing.allocator; 11 | 12 | fn test_basic(comptime V: type) !void { 13 | const Timespec = V.Timespec; 14 | const SpawnHandle = V.task.SpawnHandle; 15 | const spawn = V.task.spawn; 16 | const sleep = V.time.sleep; 17 | const now = V.time.now; 18 | 19 | const init = struct { 20 | fn tocancel(timeout: Timespec) !void { 21 | try sleep(timeout); 22 | } 23 | 24 | fn start() !void { 25 | const childTimeout = std.time.ns_per_s; 26 | 27 | const begin = now(); 28 | 29 | var task: SpawnHandle(tocancel) = undefined; 30 | try spawn(&task, .{childTimeout}, null); 31 | defer { 32 | task.join() catch |err| switch (err) { 33 | error.TaskCancelled => {}, // we know 34 | else => @panic("unexpected error"), 35 | }; 36 | } 37 | 38 | // go async to trigger the child to run 39 | try sleep(0); 40 | 41 | // cancel the child 42 | task.cancel(); 43 | 44 | // this test should run in << 1 sec 45 | try std.testing.expect(now() - begin < childTimeout); 46 | } 47 | }.start; 48 | 49 | try V.testing.runTest(alloc, V.DefaultTestConfig, init, .{}); 50 | } 51 | 52 | test "basic cancellation" { 53 | try test_basic(vortex.Vortex); 54 | } 55 | 56 | // A cancellation torture test. Recursively spawns `fanout' number of tasks, 57 | // up to a predefined `max_levels' recursion depth. Each task sleeps for 58 | // a specified amount of time and then cancels a specified number of its 59 | // spawned children. Used by cancel-fuzz to find scheduler issues, each 60 | // of which is saved in the test driver below. 61 | pub fn TortureTask(comptime V: type, comptime options: anytype) type { 62 | return TortureTaskImpl(V, 0, options.fanout, options.max_levels); 63 | } 64 | 65 | // Turn the recursive task spawning into a compile-time recursion problem 66 | // by generating unique task entry points (exec) for each level of the 67 | // task tree. This gets around the problem of the async Frame() depending 68 | // on itself. By convention, level 0 is the root level. 69 | fn TortureTaskImpl( 70 | comptime V: type, 71 | comptime level: comptime_int, 72 | comptime fanout: comptime_int, 73 | comptime max_levels: comptime_int, 74 | ) type { 75 | const Timespec = V.Timespec; 76 | const SpawnHandle = V.task.SpawnHandle; 77 | const spawn = V.task.spawn; 78 | const sleep = V.time.sleep; 79 | const emit = V.event.emit; 80 | 81 | const Events = RegisterEvents(V, if (builtin.is_test) .debug else .info); 82 | 83 | assert(level >= 0 and level < max_levels); 84 | 85 | return struct { 86 | // How many tasks do we need across all levels? 87 | pub const num_tasks = taskCount(max_levels, fanout); 88 | pub const time_scale = std.time.ns_per_us; 89 | 90 | // returns the number of tasks given 'L' levels and fanout 'N' 91 | fn taskCount( 92 | comptime L: comptime_int, 93 | comptime N: comptime_int, 94 | ) comptime_int { 95 | // The total number of tasks is defined by the geometric series: 96 | // 1 + N + N^2 + ... + N^(L-1) = (1 - N^L) / (1 - N) 97 | assert(L >= 0); 98 | assert(N >= 1); 99 | 100 | const pow = try std.math.powi(isize, N, L); 101 | return if (N == 1) L else @divExact( 102 | 1 - pow, 103 | 1 - N, 104 | ); 105 | } 106 | 107 | pub fn exec( 108 | pidx: usize, // parent index (at its level, 0-based) 109 | sibling: usize, // sibling index (0-based) 110 | data: []const u8, // task-defining data (see below) 111 | ) anyerror!void { 112 | // which index within this level are we? 113 | const levIndex = pidx * fanout + sibling; 114 | 115 | // which index globally are we? 116 | const idx = taskCount(level, fanout) + levIndex; 117 | 118 | // extract the specifications for this task from `data' 119 | const td = struct { tocancel: usize, delay: Timespec }{ 120 | .tocancel = std.math.min(data[idx], fanout), 121 | .delay = time_scale * @as(Timespec, data[idx + num_tasks]), 122 | }; 123 | 124 | emit( 125 | Events.LevelStart, 126 | .{ .idx = idx, .lev = level, .del = td.delay }, 127 | ); 128 | 129 | if (level == max_levels - 1) { 130 | defer emit(Events.LevelReturn, .{ .idx = idx }); 131 | if (td.delay > 0) try sleep(td.delay); 132 | return; 133 | } 134 | 135 | defer emit(Events.LevelReturn, .{ .idx = idx }); 136 | 137 | const ChildTask = TortureTaskImpl(V, level + 1, fanout, max_levels); 138 | const Handle = SpawnHandle(ChildTask.exec); 139 | var tasks = [1]Handle{undefined} ** fanout; 140 | 141 | for (tasks) |*t, i| { 142 | spawn(t, .{ levIndex, i, data }, null) catch unreachable; 143 | } 144 | 145 | // Whenever we spawn a task (or tasks) we must guarantee that all 146 | // paths exiting this function 'join' the spawned task. Expect to 147 | // see a defer/errdefer after spawn every time, or else it's 148 | // probably a bug. 149 | defer { 150 | for (tasks) |*t| { 151 | t.join() catch |err| switch (err) { 152 | error.TaskCancelled => {}, // nothing to clean up 153 | else => @panic("Unexpected error"), 154 | }; 155 | } 156 | } 157 | 158 | if (td.delay > 0) { 159 | try sleep(td.delay); 160 | } 161 | 162 | var i: usize = 0; 163 | while (i < td.tocancel) : (i += 1) { 164 | emit(Events.CancelSubtree, .{ .idx = idx, .sub = i }); 165 | tasks[i].cancel(); 166 | } 167 | } 168 | }; 169 | } 170 | 171 | test "torture cancellation" { 172 | { 173 | const V = vortex.Vortex; 174 | const TT = TortureTask(vortex.Vortex, .{ .fanout = 2, .max_levels = 2 }); 175 | const data = [1]u8{0} ** (TT.num_tasks * 2); 176 | try V.testing.runTest( 177 | alloc, 178 | V.DefaultTestConfig, 179 | TT.exec, 180 | .{ 0, 0, &data }, 181 | ); 182 | } 183 | 184 | // { 185 | // // With an Autojump clock, the two child tasks end up with the same 186 | // // absolute timeout and that triggered a hang due to how the timewheel 187 | // // entries were deleted when >1 entry was in a slot. 188 | // const TT = TortureTask(.{ .fanout = 2, .max_levels = 2 }); 189 | // const data = [_]u8{ 0, 0, 0, 0, 255, 255 }; 190 | // try vx.testing.runTest( 191 | // alloc, 192 | // vx.AutojumpTestConfig, 193 | // TT.exec, 194 | // .{ 0, 0, &data }, 195 | // ); 196 | // } 197 | 198 | // { 199 | // // Cancel a task that has already completed an async op, but hasn't 200 | // // yet been rescheduled. 201 | // const TT = TortureTask(.{ .fanout = 2, .max_levels = 2 }); 202 | // const data = [_]u8{ 1, 0, 0, 255, 255, 255 }; 203 | // try vx.testing.runTest( 204 | // alloc, 205 | // vx.AutojumpTestConfig, 206 | // TT.exec, 207 | // .{ 0, 0, &data }, 208 | // ); 209 | // } 210 | } 211 | 212 | // Put the event registration behind a log-level generic function so we can vary 213 | // based on whether this is used in a unit test or a standalone fuzz test. 214 | // NOTE: this will probably break if/when we want to connect all event 215 | // registries from some top-level root package. Revisit when we get there. 216 | fn RegisterEvents(comptime V: type, log_level: std.log.Level) type { 217 | return struct { 218 | const ScopedRegistry = V.event.Registry("test.cncl", enum { 219 | level_start, 220 | level_return, 221 | cancel_subtree, 222 | }); 223 | 224 | const LevelStart = ScopedRegistry.register( 225 | .level_start, 226 | log_level, 227 | struct { idx: usize, lev: usize, del: V.Timespec }, 228 | ); 229 | 230 | const LevelReturn = ScopedRegistry.register( 231 | .level_return, 232 | log_level, 233 | struct { idx: usize }, 234 | ); 235 | 236 | const CancelSubtree = ScopedRegistry.register( 237 | .cancel_subtree, 238 | log_level, 239 | struct { idx: usize, sub: usize }, 240 | ); 241 | }; 242 | } 243 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/common/TracySystem.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | # pragma warning(disable:4996) 3 | #endif 4 | #if defined _WIN32 5 | # ifndef WIN32_LEAN_AND_MEAN 6 | # define WIN32_LEAN_AND_MEAN 7 | # endif 8 | # ifndef NOMINMAX 9 | # define NOMINMAX 10 | # endif 11 | # include 12 | # include 13 | # include "TracyUwp.hpp" 14 | #else 15 | # include 16 | # include 17 | # include 18 | #endif 19 | 20 | #ifdef __linux__ 21 | # ifdef __ANDROID__ 22 | # include 23 | # else 24 | # include 25 | # endif 26 | # include 27 | #elif defined __FreeBSD__ 28 | # include 29 | #elif defined __NetBSD__ || defined __DragonFly__ 30 | # include 31 | #endif 32 | 33 | #ifdef __MINGW32__ 34 | # define __STDC_FORMAT_MACROS 35 | #endif 36 | #include 37 | #include 38 | #include 39 | 40 | #include "TracySystem.hpp" 41 | 42 | #if defined _WIN32 43 | extern "C" typedef HRESULT (WINAPI *t_SetThreadDescription)( HANDLE, PCWSTR ); 44 | extern "C" typedef HRESULT (WINAPI *t_GetThreadDescription)( HANDLE, PWSTR* ); 45 | #endif 46 | 47 | #ifdef TRACY_ENABLE 48 | # include 49 | # include "TracyAlloc.hpp" 50 | #endif 51 | 52 | namespace tracy 53 | { 54 | 55 | namespace detail 56 | { 57 | 58 | TRACY_API uint32_t GetThreadHandleImpl() 59 | { 60 | #if defined _WIN32 61 | static_assert( sizeof( decltype( GetCurrentThreadId() ) ) <= sizeof( uint32_t ), "Thread handle too big to fit in protocol" ); 62 | return uint32_t( GetCurrentThreadId() ); 63 | #elif defined __APPLE__ 64 | uint64_t id; 65 | pthread_threadid_np( pthread_self(), &id ); 66 | return uint32_t( id ); 67 | #elif defined __ANDROID__ 68 | return (uint32_t)gettid(); 69 | #elif defined __linux__ 70 | return (uint32_t)syscall( SYS_gettid ); 71 | #elif defined __FreeBSD__ 72 | long id; 73 | thr_self( &id ); 74 | return id; 75 | #elif defined __NetBSD__ 76 | return _lwp_self(); 77 | #elif defined __DragonFly__ 78 | return lwp_gettid(); 79 | #elif defined __OpenBSD__ 80 | return getthrid(); 81 | #else 82 | // To add support for a platform, retrieve and return the kernel thread identifier here. 83 | // 84 | // Note that pthread_t (as for example returned by pthread_self()) is *not* a kernel 85 | // thread identifier. It is a pointer to a library-allocated data structure instead. 86 | // Such pointers will be reused heavily, making the pthread_t non-unique. Additionally 87 | // a 64-bit pointer cannot be reliably truncated to 32 bits. 88 | #error "Unsupported platform!" 89 | #endif 90 | 91 | } 92 | 93 | } 94 | 95 | #ifdef TRACY_ENABLE 96 | struct ThreadNameData 97 | { 98 | uint32_t id; 99 | const char* name; 100 | ThreadNameData* next; 101 | }; 102 | std::atomic& GetThreadNameData(); 103 | #endif 104 | 105 | #ifdef _MSC_VER 106 | # pragma pack( push, 8 ) 107 | struct THREADNAME_INFO 108 | { 109 | DWORD dwType; 110 | LPCSTR szName; 111 | DWORD dwThreadID; 112 | DWORD dwFlags; 113 | }; 114 | # pragma pack(pop) 115 | 116 | void ThreadNameMsvcMagic( const THREADNAME_INFO& info ) 117 | { 118 | __try 119 | { 120 | RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); 121 | } 122 | __except(EXCEPTION_EXECUTE_HANDLER) 123 | { 124 | } 125 | } 126 | #endif 127 | 128 | TRACY_API void SetThreadName( const char* name ) 129 | { 130 | #if defined _WIN32 131 | # ifdef TRACY_UWP 132 | static auto _SetThreadDescription = &::SetThreadDescription; 133 | # else 134 | static auto _SetThreadDescription = (t_SetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "SetThreadDescription" ); 135 | # endif 136 | if( _SetThreadDescription ) 137 | { 138 | wchar_t buf[256]; 139 | mbstowcs( buf, name, 256 ); 140 | _SetThreadDescription( GetCurrentThread(), buf ); 141 | } 142 | else 143 | { 144 | # if defined _MSC_VER 145 | THREADNAME_INFO info; 146 | info.dwType = 0x1000; 147 | info.szName = name; 148 | info.dwThreadID = GetCurrentThreadId(); 149 | info.dwFlags = 0; 150 | ThreadNameMsvcMagic( info ); 151 | # endif 152 | } 153 | #elif defined _GNU_SOURCE && !defined __EMSCRIPTEN__ 154 | { 155 | const auto sz = strlen( name ); 156 | if( sz <= 15 ) 157 | { 158 | #if defined __APPLE__ 159 | pthread_setname_np( name ); 160 | #else 161 | pthread_setname_np( pthread_self(), name ); 162 | #endif 163 | } 164 | else 165 | { 166 | char buf[16]; 167 | memcpy( buf, name, 15 ); 168 | buf[15] = '\0'; 169 | #if defined __APPLE__ 170 | pthread_setname_np( buf ); 171 | #else 172 | pthread_setname_np( pthread_self(), buf ); 173 | #endif 174 | } 175 | } 176 | #endif 177 | #ifdef TRACY_ENABLE 178 | { 179 | const auto sz = strlen( name ); 180 | char* buf = (char*)tracy_malloc( sz+1 ); 181 | memcpy( buf, name, sz ); 182 | buf[sz] = '\0'; 183 | auto data = (ThreadNameData*)tracy_malloc_fast( sizeof( ThreadNameData ) ); 184 | data->id = detail::GetThreadHandleImpl(); 185 | data->name = buf; 186 | data->next = GetThreadNameData().load( std::memory_order_relaxed ); 187 | while( !GetThreadNameData().compare_exchange_weak( data->next, data, std::memory_order_release, std::memory_order_relaxed ) ) {} 188 | } 189 | #endif 190 | } 191 | 192 | TRACY_API const char* GetThreadName( uint32_t id ) 193 | { 194 | static char buf[256]; 195 | #ifdef TRACY_ENABLE 196 | auto ptr = GetThreadNameData().load( std::memory_order_relaxed ); 197 | while( ptr ) 198 | { 199 | if( ptr->id == id ) 200 | { 201 | return ptr->name; 202 | } 203 | ptr = ptr->next; 204 | } 205 | #else 206 | # if defined _WIN32 207 | # ifdef TRACY_UWP 208 | static auto _GetThreadDescription = &::GetThreadDescription; 209 | # else 210 | static auto _GetThreadDescription = (t_GetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetThreadDescription" ); 211 | # endif 212 | if( _GetThreadDescription ) 213 | { 214 | auto hnd = OpenThread( THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)id ); 215 | if( hnd != 0 ) 216 | { 217 | PWSTR tmp; 218 | _GetThreadDescription( hnd, &tmp ); 219 | auto ret = wcstombs( buf, tmp, 256 ); 220 | CloseHandle( hnd ); 221 | if( ret != 0 ) 222 | { 223 | return buf; 224 | } 225 | } 226 | } 227 | # elif defined __linux__ 228 | int cs, fd; 229 | char path[32]; 230 | # ifdef __ANDROID__ 231 | int tid = gettid(); 232 | # else 233 | int tid = (int) syscall( SYS_gettid ); 234 | # endif 235 | snprintf( path, sizeof( path ), "/proc/self/task/%d/comm", tid ); 236 | sprintf( buf, "%" PRIu32, id ); 237 | # ifndef __ANDROID__ 238 | pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &cs ); 239 | # endif 240 | if ( ( fd = open( path, O_RDONLY ) ) > 0) { 241 | int len = read( fd, buf, 255 ); 242 | if( len > 0 ) 243 | { 244 | buf[len] = 0; 245 | if( len > 1 && buf[len-1] == '\n' ) 246 | { 247 | buf[len-1] = 0; 248 | } 249 | } 250 | close( fd ); 251 | } 252 | # ifndef __ANDROID__ 253 | pthread_setcancelstate( cs, 0 ); 254 | # endif 255 | return buf; 256 | # endif 257 | #endif 258 | sprintf( buf, "%" PRIu32, id ); 259 | return buf; 260 | } 261 | 262 | TRACY_API const char* GetEnvVar( const char* name ) 263 | { 264 | #if defined _WIN32 265 | // unfortunately getenv() on Windows is just fundamentally broken. It caches the entire 266 | // environment block once on startup, then never refreshes it again. If any environment 267 | // strings are added or modified after startup of the CRT, those changes will not be 268 | // seen by getenv(). This removes the possibility of an app using this SDK from 269 | // programmatically setting any of the behaviour controlling envvars here. 270 | // 271 | // To work around this, we'll instead go directly to the Win32 environment strings APIs 272 | // to get the current value. 273 | static char buffer[1024]; 274 | DWORD const kBufferSize = DWORD(sizeof(buffer) / sizeof(buffer[0])); 275 | DWORD count = GetEnvironmentVariableA(name, buffer, kBufferSize); 276 | 277 | if( count == 0 ) 278 | return nullptr; 279 | 280 | if( count >= kBufferSize ) 281 | { 282 | char* buf = reinterpret_cast(_alloca(count + 1)); 283 | count = GetEnvironmentVariableA(name, buf, count + 1); 284 | memcpy(buffer, buf, kBufferSize); 285 | buffer[kBufferSize - 1] = 0; 286 | } 287 | 288 | return buffer; 289 | #else 290 | return getenv(name); 291 | #endif 292 | } 293 | 294 | } 295 | 296 | #ifdef __cplusplus 297 | extern "C" { 298 | #endif 299 | 300 | TRACY_API void ___tracy_set_thread_name( const char* name ) { tracy::SetThreadName( name ); } 301 | 302 | #ifdef __cplusplus 303 | } 304 | #endif 305 | -------------------------------------------------------------------------------- /deps/ztracy/libs/tracy/libbacktrace/backtrace.hpp: -------------------------------------------------------------------------------- 1 | /* backtrace.h -- Public header file for stack backtrace library. 2 | Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 | Written by Ian Lance Taylor, Google. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | (1) Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | (3) The name of the author may not be used to 18 | endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. */ 32 | 33 | #ifndef BACKTRACE_H 34 | #define BACKTRACE_H 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | namespace tracy 41 | { 42 | 43 | /* The backtrace state. This struct is intentionally not defined in 44 | the public interface. */ 45 | 46 | struct backtrace_state; 47 | 48 | /* The type of the error callback argument to backtrace functions. 49 | This function, if not NULL, will be called for certain error cases. 50 | The DATA argument is passed to the function that calls this one. 51 | The MSG argument is an error message. The ERRNUM argument, if 52 | greater than 0, holds an errno value. The MSG buffer may become 53 | invalid after this function returns. 54 | 55 | As a special case, the ERRNUM argument will be passed as -1 if no 56 | debug info can be found for the executable, or if the debug info 57 | exists but has an unsupported version, but the function requires 58 | debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in 59 | this case will be something along the lines of "no debug info". 60 | Similarly, ERRNUM will be passed as -1 if there is no symbol table, 61 | but the function requires a symbol table (e.g., backtrace_syminfo). 62 | This may be used as a signal that some other approach should be 63 | tried. */ 64 | 65 | typedef void (*backtrace_error_callback) (void *data, const char *msg, 66 | int errnum); 67 | 68 | /* Create state information for the backtrace routines. This must be 69 | called before any of the other routines, and its return value must 70 | be passed to all of the other routines. FILENAME is the path name 71 | of the executable file; if it is NULL the library will try 72 | system-specific path names. If not NULL, FILENAME must point to a 73 | permanent buffer. If THREADED is non-zero the state may be 74 | accessed by multiple threads simultaneously, and the library will 75 | use appropriate atomic operations. If THREADED is zero the state 76 | may only be accessed by one thread at a time. This returns a state 77 | pointer on success, NULL on error. If an error occurs, this will 78 | call the ERROR_CALLBACK routine. 79 | 80 | Calling this function allocates resources that cannot be freed. 81 | There is no backtrace_free_state function. The state is used to 82 | cache information that is expensive to recompute. Programs are 83 | expected to call this function at most once and to save the return 84 | value for all later calls to backtrace functions. */ 85 | 86 | extern struct backtrace_state *backtrace_create_state ( 87 | const char *filename, int threaded, 88 | backtrace_error_callback error_callback, void *data); 89 | 90 | /* The type of the callback argument to the backtrace_full function. 91 | DATA is the argument passed to backtrace_full. PC is the program 92 | counter. FILENAME is the name of the file containing PC, or NULL 93 | if not available. LINENO is the line number in FILENAME containing 94 | PC, or 0 if not available. FUNCTION is the name of the function 95 | containing PC, or NULL if not available. This should return 0 to 96 | continuing tracing. The FILENAME and FUNCTION buffers may become 97 | invalid after this function returns. */ 98 | 99 | typedef int (*backtrace_full_callback) (void *data, uintptr_t pc, uintptr_t lowaddr, 100 | const char *filename, int lineno, 101 | const char *function); 102 | 103 | /* Get a full stack backtrace. SKIP is the number of frames to skip; 104 | passing 0 will start the trace with the function calling 105 | backtrace_full. DATA is passed to the callback routine. If any 106 | call to CALLBACK returns a non-zero value, the stack backtrace 107 | stops, and backtrace returns that value; this may be used to limit 108 | the number of stack frames desired. If all calls to CALLBACK 109 | return 0, backtrace returns 0. The backtrace_full function will 110 | make at least one call to either CALLBACK or ERROR_CALLBACK. This 111 | function requires debug info for the executable. */ 112 | 113 | extern int backtrace_full (struct backtrace_state *state, int skip, 114 | backtrace_full_callback callback, 115 | backtrace_error_callback error_callback, 116 | void *data); 117 | 118 | /* The type of the callback argument to the backtrace_simple function. 119 | DATA is the argument passed to simple_backtrace. PC is the program 120 | counter. This should return 0 to continue tracing. */ 121 | 122 | typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc); 123 | 124 | /* Get a simple backtrace. SKIP is the number of frames to skip, as 125 | in backtrace. DATA is passed to the callback routine. If any call 126 | to CALLBACK returns a non-zero value, the stack backtrace stops, 127 | and backtrace_simple returns that value. Otherwise 128 | backtrace_simple returns 0. The backtrace_simple function will 129 | make at least one call to either CALLBACK or ERROR_CALLBACK. This 130 | function does not require any debug info for the executable. */ 131 | 132 | extern int backtrace_simple (struct backtrace_state *state, int skip, 133 | backtrace_simple_callback callback, 134 | backtrace_error_callback error_callback, 135 | void *data); 136 | 137 | /* Print the current backtrace in a user readable format to a FILE. 138 | SKIP is the number of frames to skip, as in backtrace_full. Any 139 | error messages are printed to stderr. This function requires debug 140 | info for the executable. */ 141 | 142 | extern void backtrace_print (struct backtrace_state *state, int skip, FILE *); 143 | 144 | /* Given PC, a program counter in the current program, call the 145 | callback function with filename, line number, and function name 146 | information. This will normally call the callback function exactly 147 | once. However, if the PC happens to describe an inlined call, and 148 | the debugging information contains the necessary information, then 149 | this may call the callback function multiple times. This will make 150 | at least one call to either CALLBACK or ERROR_CALLBACK. This 151 | returns the first non-zero value returned by CALLBACK, or 0. */ 152 | 153 | extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, 154 | backtrace_full_callback callback, 155 | backtrace_error_callback error_callback, 156 | void *data); 157 | 158 | /* The type of the callback argument to backtrace_syminfo. DATA and 159 | PC are the arguments passed to backtrace_syminfo. SYMNAME is the 160 | name of the symbol for the corresponding code. SYMVAL is the 161 | value and SYMSIZE is the size of the symbol. SYMNAME will be NULL 162 | if no error occurred but the symbol could not be found. */ 163 | 164 | typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc, 165 | const char *symname, 166 | uintptr_t symval, 167 | uintptr_t symsize); 168 | 169 | /* Given ADDR, an address or program counter in the current program, 170 | call the callback information with the symbol name and value 171 | describing the function or variable in which ADDR may be found. 172 | This will call either CALLBACK or ERROR_CALLBACK exactly once. 173 | This returns 1 on success, 0 on failure. This function requires 174 | the symbol table but does not require the debug info. Note that if 175 | the symbol table is present but ADDR could not be found in the 176 | table, CALLBACK will be called with a NULL SYMNAME argument. 177 | Returns 1 on success, 0 on error. */ 178 | 179 | extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr, 180 | backtrace_syminfo_callback callback, 181 | backtrace_error_callback error_callback, 182 | void *data); 183 | 184 | } 185 | 186 | #endif 187 | --------------------------------------------------------------------------------