| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module coroutines |
| 5 | |
| 6 | import v.util |
| 7 | import time |
| 8 | |
| 9 | #flag -I @VEXEROOT/thirdparty/photon |
| 10 | #flag @VEXEROOT/thirdparty/photon/photonwrapper.so |
| 11 | #include "photonwrapper.h" |
| 12 | |
| 13 | $if windows { |
| 14 | #include "processthreadsapi.h" |
| 15 | } $else { |
| 16 | #include <pthread.h> |
| 17 | } |
| 18 | #flag -I @VEXEROOT/vlib/coroutines |
| 19 | #include "sp_corrector.c" |
| 20 | |
| 21 | fn C.set_photon_thread_stack_allocator(fn (voidptr, int) voidptr, fn (voidptr, voidptr, int)) |
| 22 | |
| 23 | // fn C.default_photon_thread_stack_alloc(voidptr, int) voidptr |
| 24 | // fn C.default_photon_thread_stack_dealloc(voidptr, voidptr, int) |
| 25 | fn C.new_photon_work_pool(i32) voidptr |
| 26 | fn C.delete_photon_work_pool() |
| 27 | fn C.init_photon_work_pool(i32) |
| 28 | fn C.photon_set_log_output_stdout() |
| 29 | fn C.photon_set_log_output_stderr() |
| 30 | fn C.photon_set_log_output_null() |
| 31 | fn C.photon_join_current_thread_into_workpool() i32 |
| 32 | fn C.photon_thread_create_and_migrate_to_work_pool(f voidptr, arg voidptr) |
| 33 | fn C.photon_thread_create(f voidptr, arg voidptr, stack_size u64) |
| 34 | fn C.photon_thread_migrate() |
| 35 | |
| 36 | // fn C.photon_thread_migrate(work_pool voidptr) |
| 37 | fn C.photon_init_default() i32 |
| 38 | fn C.photon_sleep_s(n i32) |
| 39 | fn C.photon_sleep_ms(n i32) |
| 40 | |
| 41 | fn C.sp_corrector(voidptr, voidptr) |
| 42 | |
| 43 | // sleep is coroutine-safe version of time.sleep() |
| 44 | pub fn sleep(duration time.Duration) { |
| 45 | C.photon_sleep_ms(duration.milliseconds()) |
| 46 | } |
| 47 | |
| 48 | fn alloc(_ voidptr, stack_size int) voidptr { |
| 49 | unsafe { |
| 50 | stack_ptr := malloc(stack_size) |
| 51 | |
| 52 | $if gcboehm ? { |
| 53 | // TODO: this wont work if a coroutine gets moved to a different |
| 54 | // thread, so we are using `C.GC_set_sp_corrector` with our own |
| 55 | // corrector function which seems to be the best solution for now. |
| 56 | // It would probably be more performant if we could hook into photon's context |
| 57 | // switching code (currently not possible) or we write our own implementation. |
| 58 | // TODO: update - I'm not sure if what I wrote above is correct. test |
| 59 | // C.GC_set_stackbottom(0, stack_ptr) |
| 60 | |
| 61 | C.GC_add_roots(stack_ptr, charptr(stack_ptr) + stack_size) |
| 62 | } |
| 63 | |
| 64 | return stack_ptr |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | fn dealloc(_ voidptr, stack_ptr voidptr, stack_size int) { |
| 69 | unsafe { |
| 70 | $if gcboehm ? { |
| 71 | C.GC_remove_roots(stack_ptr, charptr(stack_ptr) + stack_size) |
| 72 | } |
| 73 | free(stack_ptr) |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | fn init_photon_vcpu() { |
| 78 | C.photon_init_default() |
| 79 | $if gcboehm ? { |
| 80 | mut sb := C.GC_stack_base{} |
| 81 | C.GC_get_stack_base(&sb) |
| 82 | C.GC_register_my_thread(&sb) |
| 83 | } |
| 84 | C.photon_join_current_thread_into_workpool() |
| 85 | $if gcboehm ? { |
| 86 | C.GC_unregister_my_thread() |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | fn init() { |
| 91 | $if gcboehm ? { |
| 92 | // NOTE `sp_corrector` only works for platforms with the stack growing down |
| 93 | // MacOs, Win32 and Linux always have stack growing down. |
| 94 | // A proper solution is planned (hopefully) for boehm v8.4.0. |
| 95 | C.GC_set_sp_corrector(C.sp_corrector) |
| 96 | if C.GC_get_sp_corrector() == unsafe { nil } { |
| 97 | panic('stack pointer correction unsupported') |
| 98 | } |
| 99 | } |
| 100 | C.photon_set_log_output_null() |
| 101 | C.set_photon_thread_stack_allocator(alloc, dealloc) |
| 102 | ret := C.photon_init_default() |
| 103 | |
| 104 | if util.nr_jobs >= 1 { |
| 105 | // automatic |
| 106 | // C.init_photon_work_pool(util.nr_jobs) |
| 107 | // manual - pass 0 because we will start our own |
| 108 | C.init_photon_work_pool(0) |
| 109 | // start our own vcpu's manually |
| 110 | for _ in 1 .. util.nr_jobs { |
| 111 | spawn init_photon_vcpu() |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if ret < 0 { |
| 116 | panic_n('failed to initialize coroutines via photon ret:', ret) |
| 117 | } |
| 118 | } |
| 119 | |