Golang: Combining Signals by OR, AND
Tags: go, channel, signal
Published: 2018-08-16 04:26:07

---
title: Golang: Combining Signals by OR, AND
published: true
description: 
tags: go, channel, signal
---

In Golang if we want to avoid goroutine leaks, usually we use a special *done* channel to signal to a specific goroutine that it needs to be closed. And if we work with many modules which have it’s own done channels then we may want to combine those signals into one signal in form of OR or AND logic

### The OR combining signals
> If **any** of the signals is received then emit the combined signal

The implementation below use a number of 2 is maximum channels to be combined, you can change to any number you want:

```
// OR function combines all signal from channels into a single channel with Or condition
func OR(channels ...<-chan interface{}) <-chan interface{} {
	switch len(channels) {
	case 0:
		return nil
	case 1:
		return channels[0]
	}
	orDone := make(chan interface{})
	go func() {
		defer close(orDone)
		switch len(channels) {
		case 2:
			select {
			case <-channels[0]:
			case <-channels[1]:
			}
		default:
			select {
			case <-channels[0]:
			case <-channels[1]:
			case <-channels[2]:
			case <-OR(append(channels[3:], orDone)...):
			}
		}
	}()
	return orDone
}
```

The first case of switch statement is check if length of channels is 0, we will block forever by returning a **nil** channel, this is correct behavior when there no signal at all

The second case of switch statement is if only one channel is passed into this function we will simply return it!

Then we create a **orChan** for combining all signals from channel, then fork a new goroutine for combine at most 2 channel into **orChan**

Notice that when we recursively call **OR** function, we pass **orChan** again to this function because of logic OR, we need to respect the **orChan** from previous combination

### The AND combining signals
> If **all** of the signals is received then emit the combined signal

The implementation use a very excited knowledge in Golang: in the select statement if right-hand side of a written channel is a receiving channel , the select statement will wait for all receiving channels to be complete before sending to the left-hand side channel

```
// AND function combines all signal from channels into a single channel with And condition
func AND(channels ...<-chan interface{}) <-chan interface{} {
	switch len(channels) {
	case 0:
		return nil
	case 1:
		return channels[0]
	}
	andDone := make(chan interface{})
	collector := make(chan interface{}, len(channels))
	go func() {
		defer close(andDone)
		switch len(channels) {
		case 2:
			select {
			case collector <- <-channels[0]:
			case collector <- <-channels[1]:
			}
		default:
			select {
			case collector <- <-channels[0]:
			case collector <- <-channels[1]:
			case collector <- <-channels[2]:
			case collector <- <-AND(channels[3:]...):
			}
		}
	}()
	return andDone
}
```

It’s very similar to the OR implementation version with 3 important different points:

* We create a buffered **collector** channel for collecting signals from combining channels

* We pass **collector** channel as left-hand side in *select* statement

* We recursively call **AND** function without pass in **andChan** into it’s parameter list, because we’re waiting for all signals, there no need to respect to this signal again

I implemented those function into this [Repo](https://github.com/hauxe/GoM/blob/master/event/combine_signal.go), feel free to use it by yourself! Thanks