diff --git a/include/coz.h b/include/coz.h index 6f4bf1c..dc0bcc8 100644 --- a/include/coz.h +++ b/include/coz.h @@ -37,29 +37,55 @@ typedef struct { size_t backoff; // Used to batch updates to the shared counter. Currently unused. } coz_counter_t; -// The type of the _coz_get_counter function -typedef coz_counter_t* (*coz_get_counter_t)(int, const char*); - -// Locate and invoke _coz_get_counter static coz_counter_t* _call_coz_get_counter(int type, const char* name) { - static unsigned char _initialized = 0; - static coz_get_counter_t fn; // The pointer to _coz_get_counter + // Use memcpy to avoid pedantic GCC complaint about storing function pointer in void* + static unsigned char initialized = dlsym == NULL; + static coz_counter_t* (*fn)(int, const char*); + + if (!initialized) { + void* p = dlsym(RTLD_DEFAULT, "_coz_get_counter"); + memcpy(&fn, &p, sizeof(p)); + initialized = true; + } - if(!_initialized) { - if(dlsym) { - // Locate the _coz_get_counter method - void* p = dlsym(RTLD_DEFAULT, "_coz_get_counter"); + if (fn) return fn(type, name); + else return 0; +} - // Use memcpy to avoid pedantic GCC complaint about storing function pointer in void* - memcpy(&fn, &p, sizeof(p)); - } +static void _call_coz_pre_block() { + static unsigned char initialized = dlsym == 0; + static void (*fn)(); - _initialized = 1; + if (!initialized) { + void* p = dlsym(RTLD_DEFAULT, "_coz_pre_block"); + memcpy(&fn, &p, sizeof(p)); + initialized = true; } + if (fn) return fn(); +} - // Call the function, or return null if profiler is not found - if(fn) return fn(type, name); - else return 0; +static void _call_coz_post_block(bool skip_delays) { + static unsigned char initialized = dlsym == 0; + static void (*fn)(bool); + + if (!initialized) { + void* p = dlsym(RTLD_DEFAULT, "_coz_post_block"); + memcpy(&fn, &p, sizeof(p)); + initialized = true; + } + if (fn) return fn(skip_delays); +} + +static void _call_coz_wake_other() { + static unsigned char initialized = dlsym == 0; + static void (*fn)(); + + if (!initialized) { + void* p = dlsym(RTLD_DEFAULT, "_coz_wake_other"); + memcpy(&fn, &p, sizeof(p)); + initialized = true; + } + if (fn) return fn(); } // Macro to initialize and increment a counter @@ -80,12 +106,80 @@ static coz_counter_t* _call_coz_get_counter(int type, const char* name) { #define STR2(x) #x #define STR(x) STR2(x) +/// Indicate progress for the counter with the given name. +/// +/// Example: +/// ``` +/// while (more_events()) { +/// COZ_PROGRESS_NAMED("event_processed"); +/// process_event(); +/// } +/// ``` #define COZ_PROGRESS_NAMED(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, name) +/// Indicate progress for the counter named implicitly after the file and line +/// number it is placed on. +/// +/// Example: +/// ``` +/// while (more_events()) { +/// COZ_PROGRESS; +/// process_event(); +/// } +/// ``` #define COZ_PROGRESS COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_THROUGHPUT, __FILE__ ":" STR(__LINE__)) #define COZ_BEGIN(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_BEGIN, name) #define COZ_END(name) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_END, name) +/// Call before (possibly) blocking (e.g. if you're about to maybe block on a +/// futex). +/// +/// Example: +/// ``` +/// lock() { +/// int expected = 0; +/// if (!std::atomic_compare_exchange_weak(&futex, &expected, 1)) { +/// COZ_PRE_BLOCK(); +/// syscall(SYS_futex, &futex, FUTEX_WAIT, ...); +/// COZ_POST_BLOCK(); +/// } +/// } +/// ``` +#define COZ_PRE_BLOCK() _call_coz_pre_block() + +/// Call after unblocking. If skip_delays is true, all delays inserted during +/// the blocked period will be skipped. +/// +/// Example: +/// ``` +/// lock() { +/// int expected = 0; +/// if (!std::atomic_compare_exchange_weak(&futex, &expected, 1)) { +/// COZ_PRE_BLOCK(); +/// syscall(SYS_futex, &futex, FUTEX_WAIT, ...); +/// COZ_POST_BLOCK(); +/// } +/// } +/// ``` +#define COZ_POST_BLOCK(skip_delays) _call_coz_post_block(skip_delays) + +/// Ensure a thread has executed all the required delays before possibly +/// unblocking another thread. +/// +/// Example: +/// ``` +/// // Unlocking the futex. +/// unlock() { +/// let have_waiters = ...; +/// std::atomic_store(&futex, 0); +/// if (have_waiters) { +/// COZ_WAKE_OTHER(); +/// syscall(SYS_FUTEX, &futex, FUTEX_WAKE, ...); +/// } +/// } +/// ``` +#define COZ_WAKE_OTHER() _call_coz_wake_other() + #if defined(__cplusplus) } #endif diff --git a/libcoz/libcoz.cpp b/libcoz/libcoz.cpp index 67daff9..5ba2300 100644 --- a/libcoz/libcoz.cpp +++ b/libcoz/libcoz.cpp @@ -63,6 +63,18 @@ extern "C" coz_counter_t* _coz_get_counter(progress_point_type t, const char* na } } +extern "C" void _coz_pre_block() { + profiler::get_instance().pre_block(); +} + +extern "C" void _coz_post_block(bool skip_delays) { + profiler::get_instance().post_block(skip_delays); +} + +extern "C" void _coz_wake_other() { + profiler::get_instance().catch_up(); +} + /** * Read a link's contents and return it as a string */ diff --git a/libcoz/profiler.h b/libcoz/profiler.h index 0998a22..2ae6c97 100644 --- a/libcoz/profiler.h +++ b/libcoz/profiler.h @@ -160,7 +160,8 @@ class profiler { state->pre_block_time = _global_delay.load(); } - /// Call after unblocking. If by_thread is true, delays will be skipped + /// Call after unblocking. If skip_delays is true, all delays inserted + /// during the blocked period will be skipped. void post_block(bool skip_delays) { thread_state* state = get_thread_state(); if(!state)