When code can run on both main and IO threads, require an explicit ownership + synchronization rule for every shared piece of state (flags, counters, refcounts, pending lists, and memory frees). Concretely:
1) No racy reads/writes of shared fields
2) Main-thread only for shared sliding-window / non-thread-safe maintenance
3) Refcount changes must be atomic end-to-end
4) If deallocation/free callbacks are not thread-safe: defer to the safe thread
Example patterns
A) IO thread signals main using a pending flag instead of touching racy state
if (!(c->io_flags & CLIENT_IO_READ_ENABLED)) {
/* No racy reads of c->flags; just signal via atomic pending flag */
atomicSetWithSync(c->pending_read, 1);
return;
}
B) Main-thread-only maintenance
atomicIncr(server.stat_total_client_process_input_buff_events, 1);
if (c->running_tid == IOTHREAD_MAIN_THREAD_ID)
statsUpdateActiveClients(c);
C) Safe atomic refcount decrement
unsigned short new_refcnt = atomicDecr(o->refcount, 1);
if (new_refcnt == 0) {
/* safe to free */
}
D) Deferred free from IO thread to main
/* IO thread */
ioDeferFreeRobj(c, obj);
/* main thread when client is back */
freeClientIODeferredObjects(c, /*free_array=*/0);
Documentation requirement
Enter the URL of a public GitHub repository