Page Content

Tutorials

What Is Mean By Context Package With Cancellation In GoLang

Context Package With Cancellation in GoLang

The main purpose of the Go context package is to specify the type of context and enable cancellation there are three types of Context packages in go those are Context Package With Cancellation, Timeouts and Deadlines . When you need to stop ongoing processes that are taking longer than anticipated, like network requests or lengthy chores, this is really helpful. Version 1.7 made the package a normal Go package.

Four methods are available in the Context type interface: Deadline(), Done(), Err(), and Value(). Functions like context are usually used to alter an existing Context variable rather than explicitly implementing these methods.context, WithCancel().By using context or WithTimeout().Use WithDeadline().

Cancellation Mechanisms

The context package can be used in the following primary methods to implement cancellation:

context.WithCancel():

  • This function generates a child Context that has the ability to cancel.
  • A cancel function (of type context.CancelFunc) and the updated Context are both returned.
  • When the cancel() function is explicitly used, or if the parent context’s Done channel is closed, the child context’s Done channel will also be closed.
  • By using cancel(), related resources are guaranteed to be released.

context.WithTimeout():

  • This function generates a child Context that, after a predetermined amount of time, automatically terminates.
  • Time and an existing context are required.The duration parameter.
  • When this context’s timeout period is up, the cancel() function is automatically called.

context.WithDeadline():

  • Though it takes a specified amount of time rather than a duration, this function generates a child Context that immediately cancels, much like WithTimeout().The deadline time.
  • When the deadline has passed, the cancel() method is invoked automatically.

A channel is returned by the Done() method of a Context when it is cancelled, and it is then closed. A choose statement can be used to respond to the cancellation by waiting for this signal. Context will then return the reason for the cancellation, such as “context cancelled” or “context deadline exceeded,” via its Err() method.

Code Examples for Cancellation

Here is an example of these cancellation strategies using a program (simpleContext.go). The three functions defined by this program, f1, f2, and f3, each represent a distinct cancellation technique.

Let’s start with the standard configuration:

package main
import (
	"context"
	"fmt"
	"os"
	"strconv"
	"time"
)
// The main function takes a delay as a command-line argument.
func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: go run <filename.go> <delay_in_seconds>")
		return
	}
	// Correctly access the first argument at index 1
	delay, err := strconv.Atoi(os.Args[1])
	if err != nil {
		fmt.Println("Error: Invalid delay argument. Must be an integer.")
		return
	}
	
	fmt.Println("Delay:", delay, "seconds")
	// Call functions demonstrating different context cancellation methods
	fmt.Println("\n--- Demonstrating context.WithCancel ---")
	f1(delay)
	
	fmt.Println("\n--- Demonstrating context.WithTimeout ---")
	f2(delay)
	
	fmt.Println("\n--- Demonstrating context.WithDeadline ---")
	f3(delay)
}
// f1 demonstrates cancellation using context.WithCancel
func f1(delay int) {
	// Create a cancellable context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Best practice: call cancel to release resources
	// Start a goroutine that listens for cancellation
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done(): // This case is triggered when the context is cancelled
				fmt.Println("f1: Goroutine received cancellation signal. Exiting.")
				return
			default:
				fmt.Println("f1: Goroutine is running...")
				time.Sleep(500 * time.Millisecond)
			}
		}
	}(ctx)
	// Simulate some work, then cancel the context
	fmt.Println("f1: Main function will cancel after", delay, "seconds.")
	time.Sleep(time.Duration(delay) * time.Second)
	
	// This will trigger the ctx.Done() channel in the goroutine
	cancel() 
	
	// Give the goroutine time to exit
	time.Sleep(1 * time.Second)
	fmt.Println("f1: Main function finished.")
}
// f2 demonstrates cancellation using context.WithTimeout
func f2(delay int) {
	// Create a context that automatically cancels after a timeout
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(delay)*time.Second)
	defer cancel() // Release resources
	// Start a goroutine that listens for cancellation
	go func(ctx context.Context) {
		select {
		case <-time.After(5 * time.Second):
			// This part will run if the goroutine's own internal work finishes
			fmt.Println("f2: Goroutine finished its work. It was NOT cancelled.")
		case <-ctx.Done(): // This case is triggered by the timeout
			fmt.Println("f2: Goroutine received cancellation signal. Cause:", ctx.Err())
		}
	}(ctx)
	// Wait for the context to finish (due to timeout or manual cancellation)
	<-ctx.Done()
	fmt.Println("f2: Main function finished.")
}
// f3 demonstrates cancellation using context.WithDeadline
func f3(delay int) {
	// Create a context that automatically cancels at a specific time (the deadline)
	deadline := time.Now().Add(time.Duration(delay) * time.Second)
	ctx, cancel := context.WithDeadline(context.Background(), deadline)
	defer cancel() // Release resources
	// Start a goroutine that listens for cancellation
	go func(ctx context.Context) {
		select {
		case <-time.After(5 * time.Second):
			// This part will run if the goroutine's internal work finishes first
			fmt.Println("f3: Goroutine finished its work. It was NOT cancelled.")
		case <-ctx.Done(): // This case is triggered by the deadline being reached
			fmt.Println("f3: Goroutine received cancellation signal. Cause:", ctx.Err())
		}
	}(ctx)
	// Wait for the context to finish
	<-ctx.Done()
	fmt.Println("f3: Main function finished.")
}

Output

Delay: 3 seconds
--- Demonstrating context.WithCancel ---
f1: Main function will cancel after 3 seconds.
f1: Goroutine is running...
f1: Goroutine is running...
f1: Goroutine is running...
f1: Goroutine is running...
f1: Goroutine is running...
f1: Goroutine is running...
f1: Goroutine received cancellation signal. Exiting.
f1: Main function finished.
--- Demonstrating context.WithTimeout ---
f2: Goroutine received cancellation signal. Cause: context deadline exceeded
f2: Main function finished.
--- Demonstrating context.WithDeadline ---
f3: Goroutine received cancellation signal. Cause: context deadline exceeded
f3: Main function finished.

f1 function

In the f1 function, context is used.A cancellable context is created using WithCancel(). Regardless of the input delay, after a predetermined 4 seconds, a goroutine is started to invoke the cancel() function.

package main
import (
	"context"
	"fmt"
	"time"
)
// f1 demonstrates the use of a cancellable context
func f1(t int) {
	// Initialize an empty Context parameter
	c1 := context.Background()
	// Create a child context with cancellation and get the cancel function
	c1, cancel := context.WithCancel(c1)
	defer cancel() // Ensure cancel is called to release resources 
	// Start a goroutine that will cancel the context after 4 seconds
	go func() {
		time.Sleep(4 * time.Second)
		cancel()
	}()
	fmt.Printf("f1(): Waiting for %d seconds or cancellation...\n", t)
	select {
	// This case receives a signal from the context's Done channel 
	case <-c1.Done():
		// The goroutine's sleep finished, and cancel() was called,
		// so c1.Done() is closed, and we can retrieve the cancellation reason.
		fmt.Println("f1(): Cancellation signal received. Reason:", c1.Err())
		return
	// This case will be triggered if the delay 't' passes before cancellation
	case <-time.After(time.Duration(t) * time.Second):
		fmt.Printf("f1(): The specified delay of %d seconds passed.\n", t)
		return
	}
}
// Example main function to demonstrate f1
func main() {
	// Case 1: The delay (5s) is longer than the cancellation timeout (4s).
	// The goroutine will trigger cancellation.
	fmt.Println("Running f1 with a 5-second delay...")
	f1(5)
	fmt.Println("--------------------")
	// Case 2: The delay (3s) is shorter than the cancellation timeout (4s).
	// The time.After channel will be triggered first.
	fmt.Println("Running f1 with a 3-second delay...")
	f1(3)
}

Output

Running f1 with a 5-second delay...
f1(): Waiting for 5 seconds or cancellation...
f1(): Cancellation signal received. Reason: context canceled
--------------------
Running f1 with a 3-second delay...
f1(): Waiting for 3 seconds or cancellation...
f1(): The specified delay of 3 seconds passed.

Behavior: The time if the delay given to F1 is less than or equal to 4 seconds.It’s possible that the program prints the time after the case triggers first. Otherwise, c1 will be closed when the goroutine calls cancel() if the delay exceeds 4 seconds.Done() and “f1(): context cancelled” are printed.

f2 function

Context is employed by the f2 function.An automatic cancellation can be set using WithTimeout() after a duration equal to the input delay.

package main
import (
	"context"
	"fmt"
	"time"
)
// f2 demonstrates the use of a context with a timeout
func f2(t int) {
	// The timeout is set to the provided integer `t`
	c2, cancel := context.WithTimeout(context.Background(), time.Duration(t)*time.Second)
	defer cancel() // Release resources as soon as the function returns
	// A separate goroutine to manually cancel the context after 4 seconds.
	// This demonstrates that either the timeout or the manual cancel can trigger the c2.Done() channel.
	go func() {
		time.Sleep(4 * time.Second)
		cancel() 
	}()
	fmt.Printf("f2(): Waiting for %d seconds timeout or manual cancellation...\n", t)
	// We only need to listen to the context's Done channel.
	// The context itself handles the timeout.
	select {
	case <-c2.Done():
		// The c2.Done() channel is closed when either the timeout occurs or cancel() is called.
		fmt.Println("f2(): Context done. Reason:", c2.Err())
	}
	
	// The function returns after the select block finishes.
}
// Example main function to demonstrate f2
func main() {
	// Case 1: Timeout (5s) is longer than manual cancel (4s).
	// The manual cancel will trigger first.
	fmt.Println("Running f2 with a 5-second timeout...")
	f2(5)
	
}

Output

Running f2 with a 5-second timeout...
f2(): Waiting for 5 seconds timeout or manual cancellation...
f2(): Context done. Reason: context canceled

Behavior: For instance, if the internal goroutine calls cancel() after 4 seconds and the input delay is 4 seconds, the timeout may also occur at about the same moment. The context is 10 seconds of delay.The message “f2(): context deadline exceeded” will appear when WithTimeout() cancels after 10 seconds.

f3 function

The context is shown using the f3 function.By specifying the cancellation point to a specified future time, 2*t seconds from now, WithDeadline() can be used.now().

package main
import (
	"context"
	"fmt"
	"time"
)
// f3 demonstrates the use of a context with a deadline
func f3(t int) {
	// Define a deadline: `t` seconds from now.
	// Note: the original code had 2*t, which is changed to 't' for clarity.
	deadline := time.Now().Add(time.Duration(t) * time.Second)
	
	// Create a child context with this deadline.
	c3, cancel := context.WithDeadline(context.Background(), deadline)
	defer cancel() // Ensure cancel is called to release resources
	// A separate goroutine to manually cancel the context after 4 seconds.
	// This shows that either the deadline or a manual cancel can trigger the c3.Done() channel.
	go func() {
		time.Sleep(4 * time.Second)
		cancel() 
	}()
	fmt.Printf("f3(): Waiting for deadline (in %d seconds) or manual cancellation...\n", t)
	// We only need to listen to the context's Done channel.
	// The context itself handles the deadline.
	select {
	case <-c3.Done():
		// The c3.Done() channel is closed when either the deadline is reached or cancel() is called.
		fmt.Println("f3(): Context done. Reason:", c3.Err())
	}
	// The function returns after the select block finishes.
}
// Example main function to demonstrate f3
func main() {
	// Case 1: Deadline (5s) is longer than manual cancel (4s).
	// The manual cancel will trigger first.
	fmt.Println("Running f3 with a 5-second deadline...")
	f3(5)
	
}

Output

Running f3 with a 5-second deadline...
f3(): Waiting for deadline (in 5 seconds) or manual cancellation...
f3(): Context done. Reason: context canceled

Behavior: A deadline of 2*t has been set. The time depends on how little the delay (t) is, such as 4 seconds.The after(t) case is probably going to start. Because the earliest cancellation point (in this case, explicit cancel() in the goroutine) is reached if t is long (for example, 10 seconds, making the deadline 20 seconds from now) and the internal goroutine attempts to cancel() after 4 seconds, “f3(): context cancelled” would result.

Advanced Example: HTTP Client Timeout

UseContext.go is another sophisticated example that shows how to time out HTTP client requests using the context package. In this case, an HTTP client might not wish to wait forever for a response from the server.

The connect function makes an HTTP request when it is run as a goroutine. It is given context.The parameter for context. Within connect, a select statement verifies two scenarios:

<-c.Done(): An error message such as “The request was cancelled!” is produced when the HTTP request is stopped by invoking tr.CancelRequest(req) in the event that the context is cancelled (for example, because the main function’s context.WithTimeout call timed out).

ok := <-data: Response processing occurs if the HTTP request is successfully completed prior to cancellation.

Using context, the useContext.go main method generates a Context with a timeout.WithTimeout(time, c).Time.Second * Duration(delay). This guarantees that, should the HTTP request (executed by the connect goroutine) surpass the designated latency, it will be terminated. This offers a reliable solution for dealing with perhaps sluggish or unreliable servers.

You can also read Race Conditions In Go And How To Prevent Race Conditions

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index