Skip to content

Conversation

@kobi2187
Copy link
Contributor

The channel implementation had unsafe unsynchronized access to the mask field across threads:

  1. tryRecv() checked q.mask != ChannelDeadMask BEFORE acquiring the lock
  2. sendImpl() checked q.mask == ChannelDeadMask BEFORE acquiring the lock
  3. rawSend() modified q.mask WHILE HOLDING the lock

This created a race condition detected by ThreadSanitizer where one thread reads the mask while another thread modifies it under lock protection.

The fix moves both mask checks inside their respective critical sections:

  • tryRecv: Now acquires lock first, then checks mask
  • sendImpl: Now acquires lock first, then checks mask (releasing before fatal)

This ensures all accesses to the shared mask field are properly synchronized.

Fixes #23174

The channel implementation had unsafe unsynchronized access to the `mask` field across threads: 1. tryRecv() checked `q.mask != ChannelDeadMask` BEFORE acquiring the lock 2. sendImpl() checked `q.mask == ChannelDeadMask` BEFORE acquiring the lock 3. rawSend() modified `q.mask` WHILE HOLDING the lock This created a race condition detected by ThreadSanitizer where one thread reads the mask while another thread modifies it under lock protection. The fix moves both mask checks inside their respective critical sections: - tryRecv: Now acquires lock first, then checks mask - sendImpl: Now acquires lock first, then checks mask (releasing before fatal) This ensures all accesses to the shared `mask` field are properly synchronized. Fixes nim-lang#23174
@Araq
Copy link
Member

Araq commented Dec 17, 2025

But the idea was that a Dead channel also has no lock in a valid state. These reads must use atomics, not the lock. Use a better AI model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants