{
  "id": 9513174,
  "title": "What I learned from reading Go's chan.go",
  "url": "https://urgent.news/2026/09/24/what-i-learned-from-reading-gos-chan-go",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-24T07:11:04.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/mohsenm4/what-i-learned-from-reading-gos-chango-4jbj"
  },
  "original_language": "en",
  "account": "My understanding of channels deepened significantly after diving into Go's chan.go and select.go source files. At first, I viewed channels as a simple means for goroutines to exchange values. However, this oversimplification did not fully capture their inner workings.\n\nAfter investing a week studying the runtime/chan.go and select.go files, I discovered key insights. A channel is fundamentally a structure called hchan, accessible via a pointer from make(chan T, n). This structure comprises three main components: a ring buffer for storing values, two wait queues for goroutines blocked on send and receive operations, and a lock to manage access to these components. The lock is runtime.mutex, chosen over sync.Mutex to prevent circular dependencies.\n\nWhen dealing with an unbuffered channel (dataqsiz == 0), values are directly transferred between goroutines, making it a synchronization point rather than just a data pipe. This mechanism ensures both sides of the communication are active, preventing data loss.\n\nA send operation can end in several ways: nil channel, closed channel, a waiting receiver in the recvq, or a full buffer. In the latter case, instead of placing the waiting sender in a wait queue, the value is handed to the receiver directly, or it is placed in the buffer if space is available. If the buffer is full and a receiver is waiting, the receiver receives the oldest queued value, and the sender's value is stored in the freed slot. This ensures First-In-First-Out (FIFO) order is maintained.\n\nSudogs, short for \"synchronization objects,\" represent goroutines waiting on operations like sends or receives. Unlike queuing goroutines directly in wait queues, sudogs allow a single goroutine to wait on multiple channels simultaneously—essential for the functionality of select statements. Each run of a select statement creates a separate sudog for each case, with all pointing to the same goroutine. Upon a case becoming ready, the corresponding sudog is dequeued, and the lock order, rather than the code order, helps prevent potential deadlocks caused by selecting over channels in mixed order.\n\nThrough this exploration, I built a bounded channel using synchronization primitives like sync.Mutex and sync.Cond, reinforcing my understanding of channels' intricate mechanics.",
  "summary": "Before reading chan.go , my mental model of a channel was one sentence: a safe way for goroutines to send values to each other. That sentence was true, but it was not enough. I could not answer simple follow-up questions: where does a blocked goroutine actually wait? Is an unbuffered channel secretly a buffer of size one? Why does select pick a random case? So I spent a week reading…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}