Это многостраничный печатный вид этого раздела. Нажмите что бы печатать.

Вернуться к обычному просмотру страницы.

Описание пакета context языка программирования Go

Пакет Context определяет тип Context, который передает сроки, сигналы отмены и другие значения в рамках запроса через границы API и между процессами.

Входящие запросы к серверу должны создавать Context, а исходящие вызовы к серверам должны принимать Context. Цепочка вызовов функций между ними должна передавать Context, при необходимости заменяя его производным Context, созданным с помощью WithCancel, WithDeadline, WithTimeout или WithValue.

Контекст может быть отменен, чтобы указать, что работа, выполняемая от его имени, должна быть остановлена. Контекст с крайним сроком отменяется после истечения крайнего срока. Когда контекст отменяется, все производные от него контексты также отменяются.

Функции WithCancel, WithDeadline и WithTimeout принимают контекст (родительский) и возвращают производный контекст (дочерний) и CancelFunc. Прямой вызов CancelFunc отменяет дочерний контекст и его дочерние контексты, удаляет ссылку родительского контекста на дочерний и останавливает все связанные таймеры. Невыполнение вызова CancelFunc приводит к утечке дочернего контекста и его дочерних контекстов до тех пор, пока не будет отменен родительский контекст. Инструмент go vet проверяет, что CancelFuncs используются на всех путях управления потоком.

Функции WithCancelCause, WithDeadlineCause и WithTimeoutCause возвращают CancelCauseFunc, который принимает ошибку и записывает ее как причину отмены. Вызов Cause на отмененном контексте или любом из его дочерних элементов извлекает причину. Если причина не указана, Cause(ctx) возвращает то же значение, что и ctx.Err().

Программы, использующие контексты, должны следовать этим правилам, чтобы обеспечить согласованность интерфейсов между пакетами и позволить инструментам статического анализа проверять распространение контекста:

Не храните контексты внутри типа struct; вместо этого явно передавайте контекст каждой функции, которой он нужен. Это подробнее обсуждается в разделе «https://go.dev/blog/context-and-structs». Контекст должен быть первым параметром, обычно называемым ctx:

func DoSomething(ctx context.Context, arg Arg) error {
    // ... использовать ctx ...
}

Не передавайте nil Context, даже если функция это допускает. Передайте context.TODO, если не уверены, какой Context использовать.

Используйте значения контекста только для данных в рамках запроса, которые проходят через процессы и API, а не для передачи опциональных параметров функциям.

Один и тот же Context может передаваться функциям, выполняющимся в разных goroutines; Context безопасен для одновременного использования несколькими goroutines.

См. https://go.dev/blog/context для примера кода сервера, использующего Context.

Переменные пакета

var Canceled = errors.New(«context canceled»)

Canceled — это ошибка, возвращаемая [Context.Err], когда контекст отменяется по какой-либо причине, кроме истечения срока.

var DeadlineExceeded error = deadlineExceededError{}

DeadlineExceeded — это ошибка, возвращаемая [Context.Err], когда контекст отменяется из-за истечения срока.

1 - Описание функций пакета Context

Описание функций пакета Context: AfterFunc, Cause, WithCancel, WithCancelCause, WithDeadline, WithDeadlineCause, WithTimeout, WithTimeoutCause

func AfterFunc

func AfterFunc(ctx Context, f func()) (stop func() bool)

AfterFunc организует вызов f в собственной горутине после отмены ctx. Если ctx уже отменен, AfterFunc вызывает f сразу в своей собственной горутине.

Несколько вызовов AfterFunc на одном контексте работают независимо; один не заменяет другой.

Вызов возвращаемой функции stop прекращает ассоциацию ctx с f. Он возвращает true, если вызов остановил запуск f. Если stop возвращает false, то либо контекст отменен и f была запущена в своей собственной goroutine, либо f уже была остановлена. Функция stop не ждет завершения f перед возвратом. Если вызывающей стороне необходимо знать, завершено ли выполнение f, она должна явно согласовать это с f.

Если у ctx есть метод “AfterFunc(func()) func() bool”, AfterFunc будет использовать его для планирования вызова.

Пример (Cond)

В этом примере AfterFunc используется для определения функции, которая ожидает синхронизацию с Cond, прекращая ожидание при отмене контекста.

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func main() {
	waitOnCond := func(ctx context.Context, cond *sync.Cond, conditionMet func() bool) error {
		// Создаем канал для отмены AfterFunc
		done := make(chan struct{})
		defer close(done)

		// Настраиваем функцию отмены по таймауту контекста
		stopf := context.AfterFunc(ctx, func() {
			cond.L.Lock()
			defer cond.L.Unlock()
			select {
			case <-done:
				// Уже завершились, не нужно broadcast
				return
			default:
				// Пробуждаем все ожидающие горутины
				cond.Broadcast()
			}
		})
		defer stopf()

		// Ожидаем выполнения условия
		for !conditionMet() {
			// Проверяем контекст перед ожиданием
			if ctx.Err() != nil {
				return ctx.Err()
			}
			cond.Wait()
			// Проверяем контекст после пробуждения
			if ctx.Err() != nil {
				return ctx.Err()
			}
		}

		return nil
	}

	cond := sync.NewCond(&sync.Mutex{})

	var wg sync.WaitGroup
	start := time.Now()
	for i := 0; i < 4; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()

			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
			defer cancel()

			cond.L.Lock()
			defer cond.L.Unlock()

			err := waitOnCond(ctx, cond, func() bool { return false })
			fmt.Printf("Goroutine %d finished after %v with error: %v\n", 
				id, time.Since(start), err)
		}(i)
	}

	wg.Wait()
	fmt.Println("All goroutines completed")
}
Пример Connection

В этом примере используется AfterFunc для определения функции, которая считывает данные из net.Conn, останавливая считывание при отмене контекста.

package main

import (
	"context"
	"fmt"
	"net"
	"time"
)

func readFromConn(ctx context.Context, conn net.Conn, b []byte) (n int, err error) {
	stopc := make(chan struct{})
	defer close(stopc) // Гарантируем закрытие канала

	// Устанавливаем функцию отмены чтения при отмене контекста
	stop := context.AfterFunc(ctx, func() {
		conn.SetReadDeadline(time.Now()) // Прерываем текущее чтение
		close(stopc)
	})
	defer func() {
		if !stop() {
			// Если AfterFunc был запущен, сбрасываем дедлайн
			<-stopc
			conn.SetReadDeadline(time.Time{})
		}
	}()

	n, err = conn.Read(b)
	if ctx.Err() != nil {
		return 0, ctx.Err() // Возвращаем ошибку контекста если он отменен
	}
	return n, err
}

func main() {
	// Создаем тестовый сервер
	listener, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		fmt.Println("Failed to create listener:", err)
		return
	}
	defer listener.Close()

	// Устанавливаем соединение
	conn, err := net.Dial(listener.Addr().Network(), listener.Addr().String())
	if err != nil {
		fmt.Println("Failed to dial:", err)
		return
	}
	defer conn.Close()

	// Создаем контекст с таймаутом
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
	defer cancel()

	// Читаем данные
	b := make([]byte, 1024)
	_, err = readFromConn(ctx, conn, b)
	
	// Проверяем тип ошибки
	switch {
	case err == nil:
		fmt.Println("Read completed successfully")
	case ctx.Err() != nil:
		fmt.Println("Operation canceled:", ctx.Err())
	default:
		fmt.Println("Read error:", err)
	}
}

Дополнительные рекомендации:

  1. Таймауты соединения:

    conn.SetDeadline(time.Now().Add(30 * time.Second))
    
  2. Буферизация:

    bufReader := bufio.NewReader(conn)
    n, err = bufReader.Read(b)
    
  3. Повторные попытки:

    for retries := 0; retries < 3; retries++ {
        n, err = readFromConn(ctx, conn, b)
        if err == nil || !isTemporary(err) {
            break
        }
    }
    
  4. Логирование:

    log.Printf("Read %d bytes, error: %v", n, err)
    
Пример Merge

В этом примере AfterFunc используется для определения функции, которая объединяет сигналы отмены двух Контекстов.

package main

import (
	"context"
	"errors"
	"fmt"
	"sync"
)

func main() {
	// mergeCancel возвращает контекст, который отменяется при отмене любого из исходных контекстов
	mergeCancel := func(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) {
		mergedCtx, cancel := context.WithCancelCause(ctx1)
		var once sync.Once

		// Отслеживаем отмену первого контекста
		go func() {
			select {
			case <-ctx1.Done():
				once.Do(func() {
					cancel(context.Cause(ctx1))
				})
			case <-ctx2.Done():
				once.Do(func() {
					cancel(context.Cause(ctx2))
				})
			case <-mergedCtx.Done():
				// Уже отменен другим путем
			}
		}()

		return mergedCtx, func() {
			once.Do(func() {
				cancel(context.Canceled)
			})
		}
	}

	// Создаем два контекста с возможностью отмены
	ctx1, cancel1 := context.WithCancelCause(context.Background())
	defer cancel1(errors.New("ctx1 canceled"))

	ctx2, cancel2 := context.WithCancelCause(context.Background())

	// Объединяем контексты
	mergedCtx, mergedCancel := mergeCancel(ctx1, ctx2)
	defer mergedCancel()

	// Отменяем второй контекст
	cancel2(errors.New("ctx2 canceled"))

	// Ждем отмены объединенного контекста
	<-mergedCtx.Done()
	
	// Выводим причину отмены
	fmt.Println("Merged context canceled because:", context.Cause(mergedCtx))
}

Ключевые моменты:

  1. Потокобезопасность:

    var once sync.Once
    once.Do(func() {
        cancel(context.Cause(ctx))
    })
    
  2. Полное отслеживание контекстов:

    select {
    case <-ctx1.Done():
    case <-ctx2.Done():
    case <-mergedCtx.Done():
    }
    
  3. Четкая причина отмены:

    fmt.Println("Merged context canceled because:", context.Cause(mergedCtx))
    
  4. Гарантированная очистка:

    defer mergedCancel()
    

func Cause

func Cause(c Context) error

Cause возвращает не нулевую ошибку, объясняющую, почему c был отменен. Первая отмена c или одного из его родителей устанавливает причину. Если эта отмена произошла через вызов CancelCauseFunc(err), то Cause возвращает err. В противном случае Cause(c) возвращает то же значение, что и c.Err(). Cause возвращает nil, если c еще не был отменен.

func WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel возвращает производный контекст, который указывает на родительский контекст, но имеет новый канал Done. Канал Done возвращенного контекста закрывается при вызове возвращенной функции отмены или при закрытии канала Done родительского контекста, в зависимости от того, что произойдет раньше.

Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать отмену, как только операции, выполняемые в этом контексте, завершатся.

Пример

Этот пример демонстрирует использование отменяемого контекста для предотвращения утечки данных из горутины. К концу выполнения функции примера горутина, запущенная gen, вернется без утечки.

package main

import (
	"context"
	"fmt"
	"sync"
)

func main() {
	// gen генерирует целые числа в отдельной горутине и
	// отправляет их в возвращаемый канал.
	// Вызывающая сторона должна отменить контекст после
	// завершения потребления чисел, чтобы избежать утечки горутины.
	gen := func(ctx context.Context) <-chan int {
		dst := make(chan int)
		var wg sync.WaitGroup
		wg.Add(1)

		go func() {
			defer wg.Done()
			defer close(dst) // Закрываем канал при завершении
			n := 1
			for {
				select {
				case <-ctx.Done():
					return
				case dst <- n:
					n++
				}
			}
		}()

		// Горутина для безопасного завершения
		go func() {
			wg.Wait()
		}()

		return dst
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Отменяем контекст при завершении

	for n := range gen(ctx) {
		fmt.Println(n)
		if n == 5 {
			break
		}
	}

	// Дополнительная отмена контекста (хотя defer уже сделает это)
	cancel()
}

func WithCancelCause

func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)

WithCancelCause ведет себя как WithCancel, но возвращает CancelCauseFunc вместо CancelFunc. Вызов cancel с no-nil ошибкой (причина) записывает эту ошибку в ctx; затем ее можно получить с помощью Cause(ctx). Вызов cancel с nil устанавливает причину в Canceled.

Пример использования:

ctx, cancel := context.WithCancelCause(parent)
cancel(myError)
ctx.Err() // возвращает context.Canceled
context.Cause(ctx) // возвращает myError

func WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline возвращает производный контекст, который указывает на родительский контекст, но имеет срок, скорректированный так, чтобы он не был позднее d. Если срок родительского контекста уже раньше d, WithDeadline(parent, d) семантически эквивалентен parent. Возвращаемый канал [Context.Done] закрывается по истечении срока, при вызове возвращаемой функции cancel или при закрытии канала Done родительского контекста, в зависимости от того, что произойдет раньше.

Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать отмену, как только операции, выполняемые в этом контексте, завершатся.

Пример

В этом примере контекст с произвольным сроком передается блокирующей функции, чтобы сообщить ей, что она должна прекратить свою работу, как только до нее доберется.

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	shortDuration := 50 * time.Millisecond // Явное указание времени таймаута
	d := time.Now().Add(shortDuration)
	
	// Создаем контекст с дедлайном
	ctx, cancel := context.WithDeadline(context.Background(), d)
	defer cancel() // Важно вызывать cancel для освобождения ресурсов

	neverReady := make(chan struct{}) // Канал, который никогда не будет готов

	select {
	case <-neverReady:
		fmt.Println("ready") // Эта ветка никогда не выполнится
	case <-ctx.Done():
		// Проверяем причину завершения контекста
		switch ctx.Err() {
		case context.DeadlineExceeded:
			fmt.Println("context deadline exceeded")
		case context.Canceled:
			fmt.Println("context canceled")
		default:
			fmt.Println("context done for unknown reason")
		}
	}
}

func WithDeadlineCause

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)

WithDeadlineCause ведет себя как WithDeadline, но также устанавливает причину возвращаемого контекста при превышении срока. Возвращаемая CancelFunc не устанавливает причину.

func WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout возвращает WithDeadline(parent, time.Now().Add(timeout)).

Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать отмену, как только операции, выполняемые в этом контексте, завершатся:

func slowOperationWithTimeout(ctx context.Context) (Result, error) {
    ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
    defer cancel()  // освобождает ресурсы, если slowOperation завершается до истечения времени ожидания
	return slowOperation(ctx)
}
Пример

В этом примере контекст передается с таймаутом, чтобы сообщить блокирующей функции, что она должна завершить свою работу по истечении таймаута.

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	shortDuration := 100 * time.Millisecond // Явное указание времени таймаута

	// Создаем контекст с таймаутом
	ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
	defer cancel() // Всегда вызываем cancel для освобождения ресурсов

	neverReady := make(chan struct{}) // Создаем канал, который никогда не закроется

	select {
	case <-neverReady:
		fmt.Println("ready") // Эта ветка никогда не выполнится
	case <-ctx.Done():
		// Проверяем причину завершения контекста
		switch err := ctx.Err(); err {
		case context.DeadlineExceeded:
			fmt.Println("context deadline exceeded")
		case context.Canceled:
			fmt.Println("context canceled")
		default:
			fmt.Printf("context done: %v\n", err)
		}
	}
}

func WithTimeoutCause

func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)

WithTimeoutCause ведет себя как WithTimeout, но также устанавливает причину возвращаемого Context при истечении таймаута. Возвращаемый CancelFunc не устанавливает причину.

2 - Описание типов пакета Context

Описание типов пакета Context

type CancelCauseFunc

type CancelCauseFunc func(cause error)

CancelCauseFunc ведет себя как CancelFunc, но дополнительно устанавливает причину отмены. Эту причину можно получить, вызвав Cause на отмененном Context или на любом из его производных Context.

Если контекст уже был отменен, CancelCauseFunc не устанавливает причину. Например, если childContext является производным от parentContext:

  • если parentContext отменен с причиной cause1 до того, как childContext отменен с причиной cause2, то Cause(parentContext) == Cause(childContext) == cause1
  • если childContext отменен с причиной cause2 до того, как parentContext отменен с причиной cause1, то Cause(parentContext) == cause1 и Cause(childContext) == cause2

type CancelFunc

type CancelFunc func()

CancelFunc сообщает операции о необходимости прекратить работу. CancelFunc не ждет, пока работа будет остановлена. CancelFunc может вызываться несколькими goroutines одновременно. После первого вызова последующие вызовы CancelFunc ничего не делают.

type Context

type Context interface {
    // Deadline возвращает время, когда работа, выполняемая от имени этого 
	// контекста должна быть отменена. Deadline возвращает ok==false, если 
    // срок не установлен. Последовательные вызовы Deadline возвращают 
	// одинаковые результаты.
    Deadline() (deadline time.Time, ok bool)

    // Done возвращает канал, который закрывается, когда работа, выполняемая 
    // от имени этого контекста, должна быть отменена. Done может возвращать 
    // nil, если этот контекст никогда не может быть отменен. Последовательные 
	// вызовы Done возвращают одинаковое значение.
	// Закрытие канала Done может происходить асинхронно,
    // после возврата функции cancel.
    //
    // WithCancel организует закрытие Done при вызове cancel;
    // WithDeadline организует закрытие Done по истечении срока;
    // WithTimeout организует закрытие Done по истечении таймаута
    //.
    //
	// Done предоставляется для использования в операторах select:
    //
    //  // Stream генерирует значения с помощью DoSomething и отправляет их в out,
    //  // пока DoSomething не вернет ошибку или ctx.Done не будет закрыт.
    //  func Stream(ctx context.Context, out chan<- Value) error {
    //      for {
    //          v, err := DoSomething(ctx)
	//          if err != nil {
    //              return err
    //          }
    //          select {
    //          case <-ctx.Done():
    //              return ctx.Err()
    //          case out <- v:
    //          }
    //      }
    //  }
    //
	// См. https://blog.golang.org/pipelines для получения дополнительных примеров 
	// использования канала Done для отмены.
    Done() <-chan struct{}

    // Если Done еще не закрыт, Err возвращает nil.
    // Если Done закрыт, Err возвращает no-nil ошибку, объясняющую причину:
    // DeadlineExceeded, если срок контекста истек, или Canceled, если контекст 
	// был отменен по какой-либо другой причине.
    // После того, как Err возвращает ошибку, отличную от nil, последующие вызовы 
	// Err возвращают ту же ошибку.
    Err() error

    // Value возвращает значение, связанное с этим контекстом для ключа, или nil,
    // если с ключом не связано никакое значение. Последовательные вызовы Value с
    // одним и тем же ключом возвращают один и тот же результат.
    //
	// Используйте значения контекста только для данных в рамках запроса, которые 
	// проходят через процессы и границы API, а не для передачи опциональных 
	// параметров в функции.
    //
    // Ключ идентифицирует конкретное значение в контексте. Функции, которые хотят
    // сохранить значения в контексте, обычно выделяют ключ в глобальной
    // переменной, а затем используют этот ключ в качестве аргумента для 
	// context.WithValue и Context.Value. Ключ может быть любого типа, который 
	// поддерживает равенство;
    // пакеты должны определять ключи как неэкспортируемый тип, чтобы избежать коллизий.
    //
    // Пакеты, которые определяют ключ контекста, должны предоставлять типобезопасные 
	// аксессоры для значений, хранящихся с использованием этого ключа:
    //
    //     // Пользователь пакета определяет тип User, который хранится в контекстах.
	//     пакет user
    //
    //     import «context»
    //
    //     // User — это тип значения, хранящегося в Contexts.
    //     type User struct {...}
    //
    //     // key — это неэкспортируемый тип для ключей, определенных в этом пакете.
    //     // Это предотвращает конфликты с ключами, определенными в других пакетах.
	//     type key int
    //
    //     // userKey — ключ для значений user.User в Contexts. Он
    //     // не экспортируется; клиенты используют user.NewContext и user.FromContext
    //     // вместо прямого использования этого ключа.
    //     var userKey key
    //
    //     // NewContext возвращает новый Context, который несет значение u.
	//     func NewContext(ctx context.Context, u *User) context.Context {
    //         return context.WithValue(ctx, userKey, u)
    //     }
    //
    //     // FromContext возвращает значение User, хранящееся в ctx, если оно есть.
    //     func FromContext(ctx context.Context) (*User, bool) {
	//         u, ok := ctx.Value(userKey).(*User)
    //         return u, ok
    //     }
    Value(key any) any
}

Context несет в себе срок, сигнал отмены и другие значения через границы API.

Методы контекста могут вызываться несколькими goroutines одновременно.

func Background

func Background() Context

Background возвращает непустой Context, не равный nil. Он никогда не отменяется, не имеет значений и не имеет срока действия. Обычно он используется главной функцией, инициализацией и тестами, а также в качестве Context верхнего уровня для входящих запросов.

func TODO

func TODO() Context

TODO возвращает непустой Context, не равный nil. Код должен использовать context.TODO, когда неясно, какой Context использовать, или он еще не доступен (потому что окружающая функция еще не была расширена для приема параметра Context).

func WithValue

func WithValue(parent Context, key, val any) Context

WithValue возвращает производный контекст, который указывает на родительский Context. В производном контексте значение, связанное с ключом, является val.

Используйте контекстные значения только для данных в рамках запроса, которые проходят через процессы и API, а не для передачи опциональных параметров функциям.

Предоставленный ключ должен быть сопоставимым и не должен быть типом string или любым другим встроенным типом, чтобы избежать конфликтов между пакетами, использующими контекст. Пользователи WithValue должны определять свои собственные типы для ключей. Чтобы избежать выделения памяти при присвоении interface {}, ключи контекста часто имеют конкретный тип struct {}. В качестве альтернативы статический тип экспортированных переменных ключей контекста должен быть указателем или интерфейсом.

Пример

Этот пример демонстрирует, как можно передать значение в контекст, а также как получить его, если оно существует.

package main

import (
	"context"
	"fmt"
)

func main() {
	// Определяем пользовательский тип для ключей контекста
	type favContextKey string

	// Функция для поиска значения в контексте
	lookupValue := func(ctx context.Context, k favContextKey) {
		if v := ctx.Value(k); v != nil {
			fmt.Printf("Found value for key '%s': %v\n", k, v)
			return
		}
		fmt.Printf("Key '%s' not found in context\n", k)
	}

	// Создаем ключ и контекст со значением
	languageKey := favContextKey("language")
	ctx := context.WithValue(context.Background(), languageKey, "Go")

	// Ищем существующее значение
	lookupValue(ctx, languageKey)

	// Ищем несуществующее значение
	colorKey := favContextKey("color")
	lookupValue(ctx, colorKey)

	// Дополнительная проверка с другим типом (демонстрация безопасности)
	otherKey := "otherKey" // обычная строка, не favContextKey
	lookupValue(ctx, favContextKey(otherKey)) // конвертируем в правильный тип
}

Дополнительные рекомендации:

  1. Для production-кода:
// Лучше выносить ключи в package-level константы
const (
	LanguageKey favContextKey = "language"
	ColorKey    favContextKey = "color"
)
  1. Добавить проверку типа значения:
if v, ok := ctx.Value(k).(string); ok {
    fmt.Printf("Found string value: %s\n", v)
}
  1. Для сложных данных использовать указатели:
type configKey struct{}
ctx = context.WithValue(ctx, configKey{}, &MyConfig{...})

func WithoutCancel

func WithoutCancel(parent Context) Context

WithoutCancel возвращает производный контекст, который указывает на родительский контекст и не отменяется при отмене родительского контекста. Возвращаемый контекст не возвращает Deadline или Err, а его канал Done равен nil. Вызов Cause на возвращаемом контексте возвращает nil.