@@ -43,6 +43,12 @@ typedef coz_counter_t* (*coz_get_counter_t)(int, const char*);
4343// The type of the _coz_add_delays function
4444typedef void (* coz_add_delays_t )(void );
4545
46+ // The type of the _coz_pre_block function
47+ typedef void (* coz_pre_block_t )(void );
48+
49+ // The type of the _coz_post_block function
50+ typedef void (* coz_post_block_t )(int );
51+
4652// Locate and invoke _coz_get_counter
4753static coz_counter_t * _call_coz_get_counter (int type , const char * name ) {
4854 static unsigned char _initialized = 0 ;
@@ -88,9 +94,50 @@ static void _call_coz_add_delays(void) {
8894 if (fn ) fn ();
8995}
9096
97+ // Locate and invoke _coz_pre_block
98+ static void _call_coz_pre_block (void ) {
99+ static unsigned char _initialized = 0 ;
100+ static coz_pre_block_t fn ;
101+
102+ if (!_initialized ) {
103+ if (dlsym ) {
104+ void * p = dlsym (RTLD_DEFAULT , "_coz_pre_block" );
105+ memcpy (& fn , & p , sizeof (p ));
106+ }
107+ _initialized = 1 ;
108+ }
109+
110+ if (fn ) fn ();
111+ }
112+
113+ // Locate and invoke _coz_post_block
114+ static void _call_coz_post_block (int skip_delays ) {
115+ static unsigned char _initialized = 0 ;
116+ static coz_post_block_t fn ;
117+
118+ if (!_initialized ) {
119+ if (dlsym ) {
120+ void * p = dlsym (RTLD_DEFAULT , "_coz_post_block" );
121+ memcpy (& fn , & p , sizeof (p ));
122+ }
123+ _initialized = 1 ;
124+ }
125+
126+ if (fn ) fn (skip_delays );
127+ }
128+
129+ // On macOS, per-thread timers are not available so worker threads must check
130+ // their delay debt at progress points. On Linux, delays are already applied
131+ // in the SIGPROF handler via process_samples() -> add_delays(), so calling
132+ // add_delays() again at every progress-point hit causes double application
133+ // and TPS collapse under high concurrency.
134+ #ifdef __APPLE__
135+ # define _COZ_CHECK_DELAYS _call_coz_add_delays()
136+ #else
137+ # define _COZ_CHECK_DELAYS ((void)0)
138+ #endif
139+
91140// Macro to initialize and increment a counter, then check for pending delays.
92- // The delay check is critical on macOS where per-thread timers are not available,
93- // ensuring worker threads apply delays at progress points.
94141#define COZ_INCREMENT_COUNTER (type , name ) \
95142 if(1) { \
96143 static unsigned char _initialized = 0; \
@@ -102,7 +149,7 @@ static void _call_coz_add_delays(void) {
102149 } \
103150 if(_counter) { \
104151 __atomic_add_fetch(&_counter->count, 1, __ATOMIC_RELAXED); \
105- _call_coz_add_delays() ; \
152+ _COZ_CHECK_DELAYS ; \
106153 } \
107154 }
108155
@@ -115,6 +162,25 @@ static void _call_coz_add_delays(void) {
115162#define COZ_BEGIN (name ) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_BEGIN, name)
116163#define COZ_END (name ) COZ_INCREMENT_COUNTER(COZ_COUNTER_TYPE_END, name)
117164
165+ // Custom synchronization support.
166+ // Use these macros around blocking operations that Coz does not intercept
167+ // (e.g., custom mutexes, futex-based locks, RocksDB internal synchronization).
168+ //
169+ // COZ_PRE_BLOCK; // before blocking
170+ // my_custom_lock_acquire(&lock);
171+ // COZ_POST_BLOCK(1); // after blocking (1 = skip delays)
172+ //
173+ // // Before potentially unblocking another thread:
174+ // COZ_CATCH_UP;
175+ // my_custom_lock_release(&lock);
176+ //
177+ // COZ_POST_BLOCK(skip_delays):
178+ // skip_delays=1 when woken by another thread (e.g., mutex acquired)
179+ // skip_delays=0 when the wake may have been spurious or timed out
180+ #define COZ_PRE_BLOCK _call_coz_pre_block()
181+ #define COZ_CATCH_UP _call_coz_add_delays()
182+ #define COZ_POST_BLOCK (skip_delays ) _call_coz_post_block(skip_delays)
183+
118184#if defined(__cplusplus )
119185 }
120186#endif
0 commit comments