Исходники и презентации
This commit is contained in:
13
README.md
13
README.md
@ -1 +1,14 @@
|
|||||||
# deep_go
|
# deep_go
|
||||||
|
|
||||||
|
- Модуль 1 «Основные типы данных»
|
||||||
|
- Модуль 2 «Массивы и срезы»
|
||||||
|
- Модуль 3 «Строки»
|
||||||
|
- Модуль 4 «Словари»
|
||||||
|
- Модуль 5 «Функции»
|
||||||
|
- Модуль 6 «Структуры»
|
||||||
|
- Модуль 7 «Интерфейсы»
|
||||||
|
- Модуль 8 «Обработка ошибок»
|
||||||
|
- Модуль 9 «Дженерики и рефлексия»
|
||||||
|
- Модуль 10 «Аллокатор»
|
||||||
|
- Модуль 11 «Сборщик мусора»
|
||||||
|
- Модуль 12 «Горутины и планировщик Go»
|
||||||
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module golang_course
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
|
golang.org/x/text v0.18.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
12
go.sum
Normal file
12
go.sum
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
49
homework/allocator/homework_test.go
Normal file
49
homework/allocator/homework_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
func Defragment(memory []byte, pointers []unsafe.Pointer) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefragmentation(t *testing.T) {
|
||||||
|
var fragmentedMemory = []byte{
|
||||||
|
0xFF, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragmentedPointers = []unsafe.Pointer{
|
||||||
|
unsafe.Pointer(&fragmentedMemory[0]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[5]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[10]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[15]),
|
||||||
|
}
|
||||||
|
|
||||||
|
var defragmentedPointers = []unsafe.Pointer{
|
||||||
|
unsafe.Pointer(&fragmentedMemory[0]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[1]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[2]),
|
||||||
|
unsafe.Pointer(&fragmentedMemory[3]),
|
||||||
|
}
|
||||||
|
|
||||||
|
var defragmentedMemory = []byte{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
Defragment(fragmentedMemory, fragmentedPointers)
|
||||||
|
assert.True(t, reflect.DeepEqual(defragmentedMemory, fragmentedMemory))
|
||||||
|
assert.True(t, reflect.DeepEqual(defragmentedPointers, fragmentedPointers))
|
||||||
|
}
|
||||||
58
homework/channels/homework_test.go
Normal file
58
homework/channels/homework_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type WorkerPool struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWorkerPool(workersNumber int) *WorkerPool {
|
||||||
|
// need to implement
|
||||||
|
return &WorkerPool{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an error if the pool is full
|
||||||
|
func (wp *WorkerPool) AddTask(task func()) error {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown all workers and wait for all
|
||||||
|
// tasks in the pool to complete
|
||||||
|
func (wp *WorkerPool) Shutdown() {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkerPool(t *testing.T) {
|
||||||
|
var counter atomic.Int32
|
||||||
|
task := func() {
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
counter.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool := NewWorkerPool(2)
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 600)
|
||||||
|
assert.Equal(t, int32(2), counter.Load())
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 600)
|
||||||
|
assert.Equal(t, int32(3), counter.Load())
|
||||||
|
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
_ = pool.AddTask(task)
|
||||||
|
pool.Shutdown() // wait tasks
|
||||||
|
|
||||||
|
assert.Equal(t, int32(6), counter.Load())
|
||||||
|
}
|
||||||
74
homework/contexts/homework_test.go
Normal file
74
homework/contexts/homework_test.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrGroup(ctx context.Context) (*Group, context.Context) {
|
||||||
|
// need to implement
|
||||||
|
return &Group{}, ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Group) Go(action func() error) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Group) Wait() error {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrGroupWithoutError(t *testing.T) {
|
||||||
|
var counter atomic.Int32
|
||||||
|
group, _ := NewErrGroup(context.Background())
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
group.Go(func() error {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
counter.Add(1)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := group.Wait()
|
||||||
|
assert.Equal(t, int32(5), counter.Load())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrGroupWithError(t *testing.T) {
|
||||||
|
var counter atomic.Int32
|
||||||
|
group, ctx := NewErrGroup(context.Background())
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
group.Go(func() error {
|
||||||
|
timer := time.NewTimer(time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-timer.C:
|
||||||
|
counter.Add(1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Go(func() error {
|
||||||
|
return errors.New("error")
|
||||||
|
})
|
||||||
|
|
||||||
|
err := group.Wait()
|
||||||
|
assert.Equal(t, int32(0), counter.Load())
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
48
homework/data_types/homework_test.go
Normal file
48
homework/data_types/homework_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
func ToLittleEndian(number uint32) uint32 {
|
||||||
|
return 0 // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestСonversion(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
number uint32
|
||||||
|
result uint32
|
||||||
|
}{
|
||||||
|
"test case #1": {
|
||||||
|
number: 0x00000000,
|
||||||
|
result: 0x00000000,
|
||||||
|
},
|
||||||
|
"test case #2": {
|
||||||
|
number: 0xFFFFFFFF,
|
||||||
|
result: 0xFFFFFFFF,
|
||||||
|
},
|
||||||
|
"test case #3": {
|
||||||
|
number: 0x00FF00FF,
|
||||||
|
result: 0xFF00FF00,
|
||||||
|
},
|
||||||
|
"test case #4": {
|
||||||
|
number: 0x0000FFFF,
|
||||||
|
result: 0xFFFF0000,
|
||||||
|
},
|
||||||
|
"test case #5": {
|
||||||
|
number: 0x01020304,
|
||||||
|
result: 0x04030201,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
result := ToLittleEndian(test.number)
|
||||||
|
assert.Equal(t, test.result, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
33
homework/errors/homework_test.go
Normal file
33
homework/errors/homework_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type MultiError struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MultiError) Error() string {
|
||||||
|
// need to implement
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func Append(err error, errs ...error) *MultiError {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiError(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
err = Append(err, errors.New("error 1"))
|
||||||
|
err = Append(err, errors.New("error 2"))
|
||||||
|
|
||||||
|
expectedMessage := "2 errors occured:\n\t* error 1\t* error 2\n"
|
||||||
|
assert.EqualError(t, err, expectedMessage)
|
||||||
|
}
|
||||||
150
homework/functions/homework_test.go
Normal file
150
homework/functions/homework_test.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Map(data []int, action func(int) int) []int {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter(data []int, action func(int) bool) []int {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reduce(data []int, initial int, action func(int, int) int) int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
data []int
|
||||||
|
action func(int) int
|
||||||
|
result []int
|
||||||
|
}{
|
||||||
|
"nil numbers": {
|
||||||
|
action: func(number int) int {
|
||||||
|
return -number
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"empty numbers": {
|
||||||
|
data: []int{},
|
||||||
|
action: func(number int) int {
|
||||||
|
return -number
|
||||||
|
},
|
||||||
|
result: []int{},
|
||||||
|
},
|
||||||
|
"inc numbers": {
|
||||||
|
data: []int{1, 2, 3, 4, 5},
|
||||||
|
action: func(number int) int {
|
||||||
|
return number + 1
|
||||||
|
},
|
||||||
|
result: []int{2, 3, 4, 5, 6},
|
||||||
|
},
|
||||||
|
"double numbers": {
|
||||||
|
data: []int{1, 2, 3, 4, 5},
|
||||||
|
action: func(number int) int {
|
||||||
|
return number * number
|
||||||
|
},
|
||||||
|
result: []int{1, 4, 9, 16, 25},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
result := Map(test.data, test.action)
|
||||||
|
assert.True(t, reflect.DeepEqual(test.result, result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
data []int
|
||||||
|
action func(int) bool
|
||||||
|
result []int
|
||||||
|
}{
|
||||||
|
"nil numbers": {
|
||||||
|
action: func(number int) bool {
|
||||||
|
return number == 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"empty numbers": {
|
||||||
|
data: []int{},
|
||||||
|
action: func(number int) bool {
|
||||||
|
return number == 1
|
||||||
|
},
|
||||||
|
result: []int{},
|
||||||
|
},
|
||||||
|
"even numbers": {
|
||||||
|
data: []int{1, 2, 3, 4, 5},
|
||||||
|
action: func(number int) bool {
|
||||||
|
return number%2 == 0
|
||||||
|
},
|
||||||
|
result: []int{2, 4},
|
||||||
|
},
|
||||||
|
"positive numbers": {
|
||||||
|
data: []int{-1, -2, 1, 2},
|
||||||
|
action: func(number int) bool {
|
||||||
|
return number > 0
|
||||||
|
},
|
||||||
|
result: []int{1, 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
result := Filter(test.data, test.action)
|
||||||
|
assert.True(t, reflect.DeepEqual(test.result, result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduce(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
initial int
|
||||||
|
data []int
|
||||||
|
action func(int, int) int
|
||||||
|
result int
|
||||||
|
}{
|
||||||
|
"nil numbers": {
|
||||||
|
action: func(lhs, rhs int) int {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"empty numbers": {
|
||||||
|
data: []int{},
|
||||||
|
action: func(lhs, rhs int) int {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sum of numbers": {
|
||||||
|
data: []int{1, 2, 3, 4, 5},
|
||||||
|
action: func(lhs, rhs int) int {
|
||||||
|
return lhs + rhs
|
||||||
|
},
|
||||||
|
result: 15,
|
||||||
|
},
|
||||||
|
"sum of numbers with initial value": {
|
||||||
|
initial: 10,
|
||||||
|
data: []int{1, 2, 3, 4, 5},
|
||||||
|
action: func(lhs, rhs int) int {
|
||||||
|
return lhs + rhs
|
||||||
|
},
|
||||||
|
result: 25,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
result := Reduce(test.data, test.initial, test.action)
|
||||||
|
assert.Equal(t, test.result, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
62
homework/garbage_collector/homework_test.go
Normal file
62
homework/garbage_collector/homework_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
func Trace(stacks [][]uintptr) []uintptr {
|
||||||
|
// need to implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrace(t *testing.T) {
|
||||||
|
var heapObjects = []int{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
var heapPointer1 *int = &heapObjects[1]
|
||||||
|
var heapPointer2 *int = &heapObjects[2]
|
||||||
|
var heapPointer3 *int = nil
|
||||||
|
var heapPointer4 **int = &heapPointer3
|
||||||
|
|
||||||
|
var stacks = [][]uintptr{
|
||||||
|
{
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer1)), 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, uintptr(unsafe.Pointer(&heapObjects[0])),
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer2)), 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, uintptr(unsafe.Pointer(&heapObjects[1])),
|
||||||
|
0x00, 0x00, 0x00, uintptr(unsafe.Pointer(&heapObjects[2])),
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer4)), 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, uintptr(unsafe.Pointer(&heapObjects[3])),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pointers := Trace(stacks)
|
||||||
|
expectedPointers := []uintptr{
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer1)),
|
||||||
|
uintptr(unsafe.Pointer(&heapObjects[0])),
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer2)),
|
||||||
|
uintptr(unsafe.Pointer(&heapObjects[1])),
|
||||||
|
uintptr(unsafe.Pointer(&heapObjects[2])),
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer4)),
|
||||||
|
uintptr(unsafe.Pointer(&heapPointer3)),
|
||||||
|
uintptr(unsafe.Pointer(&heapObjects[3])),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual(expectedPointers, pointers))
|
||||||
|
}
|
||||||
56
homework/generics_and_reflection/homework_test.go
Normal file
56
homework/generics_and_reflection/homework_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string `properties:"name"`
|
||||||
|
Address string `properties:"address,omitempty"`
|
||||||
|
Age int `properties:"age"`
|
||||||
|
Married bool `properties:"married"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serialize(person Person) string {
|
||||||
|
// need to implement
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerialization(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
person Person
|
||||||
|
result string
|
||||||
|
}{
|
||||||
|
"test case with empty fields": {
|
||||||
|
result: "name=\nage=0\nmarried=false",
|
||||||
|
},
|
||||||
|
"test case with fields": {
|
||||||
|
person: Person{
|
||||||
|
Name: "John Doe",
|
||||||
|
Age: 30,
|
||||||
|
Married: true,
|
||||||
|
},
|
||||||
|
result: "name=John Doe\nage=30\nmarried=true",
|
||||||
|
},
|
||||||
|
"test case with omitempty field": {
|
||||||
|
person: Person{
|
||||||
|
Name: "John Doe",
|
||||||
|
Age: 30,
|
||||||
|
Married: true,
|
||||||
|
Address: "Paris",
|
||||||
|
},
|
||||||
|
result: "name=John Doe\naddress=Paris\nage=30\nmarried=true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
result := Serialize(test.person)
|
||||||
|
assert.Equal(t, test.result, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
63
homework/goroutines_and_scheduler/homework_test.go
Normal file
63
homework/goroutines_and_scheduler/homework_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
Identifier int
|
||||||
|
Priority int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scheduler struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScheduler() Scheduler {
|
||||||
|
// need to implement
|
||||||
|
return Scheduler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) AddTask(task Task) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) ChangeTaskPriority(taskID int, newPriority int) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) GetTask() Task {
|
||||||
|
// need to implement
|
||||||
|
return Task{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrace(t *testing.T) {
|
||||||
|
task1 := Task{Identifier: 1, Priority: 10}
|
||||||
|
task2 := Task{Identifier: 2, Priority: 20}
|
||||||
|
task3 := Task{Identifier: 3, Priority: 30}
|
||||||
|
task4 := Task{Identifier: 4, Priority: 40}
|
||||||
|
task5 := Task{Identifier: 5, Priority: 50}
|
||||||
|
|
||||||
|
scheduler := NewScheduler()
|
||||||
|
scheduler.AddTask(task1)
|
||||||
|
scheduler.AddTask(task2)
|
||||||
|
scheduler.AddTask(task3)
|
||||||
|
scheduler.AddTask(task4)
|
||||||
|
scheduler.AddTask(task5)
|
||||||
|
|
||||||
|
task := scheduler.GetTask()
|
||||||
|
assert.Equal(t, task5, task)
|
||||||
|
|
||||||
|
task = scheduler.GetTask()
|
||||||
|
assert.Equal(t, task4, task)
|
||||||
|
|
||||||
|
scheduler.ChangeTaskPriority(1, 100)
|
||||||
|
|
||||||
|
task = scheduler.GetTask()
|
||||||
|
assert.Equal(t, task1, task)
|
||||||
|
|
||||||
|
task = scheduler.GetTask()
|
||||||
|
assert.Equal(t, task3, task)
|
||||||
|
}
|
||||||
63
homework/interfaces/homework_test.go
Normal file
63
homework/interfaces/homework_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type UserService struct {
|
||||||
|
// not need to implement
|
||||||
|
NotEmptyStruct bool
|
||||||
|
}
|
||||||
|
type MessageService struct {
|
||||||
|
// not need to implement
|
||||||
|
NotEmptyStruct bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainer() *Container {
|
||||||
|
// need to implement
|
||||||
|
return &Container{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) RegisterType(name string, constructor interface{}) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Resolve(name string) (interface{}, error) {
|
||||||
|
// need to implement
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDIContainer(t *testing.T) {
|
||||||
|
container := NewContainer()
|
||||||
|
container.RegisterType("UserService", func() interface{} {
|
||||||
|
return &UserService{}
|
||||||
|
})
|
||||||
|
container.RegisterType("MessageService", func() interface{} {
|
||||||
|
return &MessageService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
userService1, err := container.Resolve("UserService")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
userService2, err := container.Resolve("UserService")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
u1 := userService1.(*UserService)
|
||||||
|
u2 := userService2.(*UserService)
|
||||||
|
assert.False(t, u1 == u2)
|
||||||
|
|
||||||
|
messageService, err := container.Resolve("MessageService")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, messageService)
|
||||||
|
|
||||||
|
paymentService, err := container.Resolve("PaymentService")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, paymentService)
|
||||||
|
}
|
||||||
83
homework/maps/homewrok_test.go
Normal file
83
homework/maps/homewrok_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type OrderedMap struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderedMap() OrderedMap {
|
||||||
|
return OrderedMap{} // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OrderedMap) Insert(key, value int) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OrderedMap) Erase(key int) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OrderedMap) Contains(key int) bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OrderedMap) Size() int {
|
||||||
|
return 0 // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OrderedMap) ForEach(action func(int, int)) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCircularQueue(t *testing.T) {
|
||||||
|
data := NewOrderedMap()
|
||||||
|
assert.Zero(t, data.Size())
|
||||||
|
|
||||||
|
data.Insert(10, 10)
|
||||||
|
data.Insert(5, 5)
|
||||||
|
data.Insert(15, 15)
|
||||||
|
data.Insert(2, 2)
|
||||||
|
data.Insert(4, 4)
|
||||||
|
data.Insert(12, 12)
|
||||||
|
data.Insert(14, 14)
|
||||||
|
|
||||||
|
assert.Equal(t, 7, data.Size())
|
||||||
|
assert.True(t, data.Contains(4))
|
||||||
|
assert.True(t, data.Contains(12))
|
||||||
|
assert.False(t, data.Contains(3))
|
||||||
|
assert.False(t, data.Contains(13))
|
||||||
|
|
||||||
|
var keys []int
|
||||||
|
expectedKeys := []int{2, 4, 5, 10, 12, 14, 15}
|
||||||
|
data.ForEach(func(key, _ int) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual(expectedKeys, keys))
|
||||||
|
|
||||||
|
data.Erase(15)
|
||||||
|
data.Erase(14)
|
||||||
|
data.Erase(2)
|
||||||
|
|
||||||
|
assert.Equal(t, 4, data.Size())
|
||||||
|
assert.True(t, data.Contains(4))
|
||||||
|
assert.True(t, data.Contains(12))
|
||||||
|
assert.False(t, data.Contains(2))
|
||||||
|
assert.False(t, data.Contains(14))
|
||||||
|
|
||||||
|
keys = nil
|
||||||
|
expectedKeys = []int{4, 5, 10, 12}
|
||||||
|
data.ForEach(func(key, _ int) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual(expectedKeys, keys))
|
||||||
|
}
|
||||||
86
homework/slices_and_arrays/homework_test.go
Normal file
86
homework/slices_and_arrays/homework_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v homework_test.go
|
||||||
|
|
||||||
|
type CircularQueue struct {
|
||||||
|
values []int
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCircularQueue(size int) CircularQueue {
|
||||||
|
return CircularQueue{} // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Push(value int) bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Pop() bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Front() int {
|
||||||
|
return -1 // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Back() int {
|
||||||
|
return -1 // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Empty() bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Full() bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCircularQueue(t *testing.T) {
|
||||||
|
const queueSize = 3
|
||||||
|
queue := NewCircularQueue(queueSize)
|
||||||
|
|
||||||
|
assert.True(t, queue.Empty())
|
||||||
|
assert.False(t, queue.Full())
|
||||||
|
|
||||||
|
assert.Equal(t, -1, queue.Front())
|
||||||
|
assert.Equal(t, -1, queue.Back())
|
||||||
|
assert.False(t, queue.Pop())
|
||||||
|
|
||||||
|
assert.True(t, queue.Push(1))
|
||||||
|
assert.True(t, queue.Push(2))
|
||||||
|
assert.True(t, queue.Push(3))
|
||||||
|
assert.False(t, queue.Push(4))
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual([]int{1, 2, 3}, queue.values))
|
||||||
|
|
||||||
|
assert.False(t, queue.Empty())
|
||||||
|
assert.True(t, queue.Full())
|
||||||
|
|
||||||
|
assert.Equal(t, 1, queue.Front())
|
||||||
|
assert.Equal(t, 3, queue.Back())
|
||||||
|
|
||||||
|
assert.True(t, queue.Pop())
|
||||||
|
assert.False(t, queue.Empty())
|
||||||
|
assert.False(t, queue.Full())
|
||||||
|
assert.True(t, queue.Push(4))
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual([]int{4, 2, 3}, queue.values))
|
||||||
|
|
||||||
|
assert.Equal(t, 2, queue.Front())
|
||||||
|
assert.Equal(t, 4, queue.Back())
|
||||||
|
|
||||||
|
assert.True(t, queue.Pop())
|
||||||
|
assert.True(t, queue.Pop())
|
||||||
|
assert.True(t, queue.Pop())
|
||||||
|
assert.False(t, queue.Pop())
|
||||||
|
|
||||||
|
assert.True(t, queue.Empty())
|
||||||
|
assert.False(t, queue.Full())
|
||||||
|
}
|
||||||
74
homework/strings/homework_test.go
Normal file
74
homework/strings/homework_test.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type COWBuffer struct {
|
||||||
|
data []byte
|
||||||
|
refs *int
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCOWBuffer(data []byte) COWBuffer {
|
||||||
|
return COWBuffer{} // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *COWBuffer) Clone() COWBuffer {
|
||||||
|
return COWBuffer{} // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *COWBuffer) Close() {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *COWBuffer) Update(index int, value byte) bool {
|
||||||
|
return false // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *COWBuffer) String() string {
|
||||||
|
return "" // need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCOWBuffer(t *testing.T) {
|
||||||
|
data := []byte{'a', 'b', 'c', 'd'}
|
||||||
|
buffer := NewCOWBuffer(data)
|
||||||
|
defer buffer.Close()
|
||||||
|
|
||||||
|
copy1 := buffer.Clone()
|
||||||
|
copy2 := buffer.Clone()
|
||||||
|
|
||||||
|
assert.Equal(t, unsafe.SliceData(data), unsafe.SliceData(buffer.data))
|
||||||
|
assert.Equal(t, unsafe.SliceData(buffer.data), unsafe.SliceData(copy1.data))
|
||||||
|
assert.Equal(t, unsafe.SliceData(copy1.data), unsafe.SliceData(copy2.data))
|
||||||
|
|
||||||
|
assert.True(t, (*byte)(unsafe.SliceData(data)) == unsafe.StringData(buffer.String()))
|
||||||
|
assert.True(t, (*byte)(unsafe.StringData(buffer.String())) == unsafe.StringData(copy1.String()))
|
||||||
|
assert.True(t, (*byte)(unsafe.StringData(copy1.String())) == unsafe.StringData(copy2.String()))
|
||||||
|
|
||||||
|
assert.True(t, buffer.Update(0, 'g'))
|
||||||
|
assert.False(t, buffer.Update(-1, 'g'))
|
||||||
|
assert.False(t, buffer.Update(4, 'g'))
|
||||||
|
|
||||||
|
assert.True(t, reflect.DeepEqual([]byte{'g', 'b', 'c', 'd'}, buffer.data))
|
||||||
|
assert.True(t, reflect.DeepEqual([]byte{'a', 'b', 'c', 'd'}, copy1.data))
|
||||||
|
assert.True(t, reflect.DeepEqual([]byte{'a', 'b', 'c', 'd'}, copy2.data))
|
||||||
|
|
||||||
|
assert.NotEqual(t, unsafe.SliceData(buffer.data), unsafe.SliceData(copy1.data))
|
||||||
|
assert.Equal(t, unsafe.SliceData(copy1.data), unsafe.SliceData(copy2.data))
|
||||||
|
|
||||||
|
copy1.Close()
|
||||||
|
|
||||||
|
previous := copy2.data
|
||||||
|
copy2.Update(0, 'f')
|
||||||
|
current := copy2.data
|
||||||
|
|
||||||
|
// 1 reference - don't need to copy buffer during update
|
||||||
|
assert.Equal(t, unsafe.SliceData(previous), unsafe.SliceData(current))
|
||||||
|
|
||||||
|
copy2.Close()
|
||||||
|
}
|
||||||
226
homework/structs/homework_test.go
Normal file
226
homework/structs/homework_test.go
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Option func(*GamePerson)
|
||||||
|
|
||||||
|
func WithName(name string) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCoordinates(x, y, z int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithGold(gold int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithMana(mana int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHealth(health int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithRespect(respect int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithStrength(strength int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithExperience(experience int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithLevel(level int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHouse() func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithGun() func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithFamily() func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithType(personType int) func(*GamePerson) {
|
||||||
|
return func(person *GamePerson) {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BuilderGamePersonType = iota
|
||||||
|
BlacksmithGamePersonType
|
||||||
|
WarriorGamePersonType
|
||||||
|
)
|
||||||
|
|
||||||
|
type GamePerson struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGamePerson(options ...Option) GamePerson {
|
||||||
|
// need to implement
|
||||||
|
return GamePerson{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Name() string {
|
||||||
|
// need to implement
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) X() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Y() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Z() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Gold() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Mana() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Health() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Respect() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Strength() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Experience() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Level() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) HasHouse() bool {
|
||||||
|
// need to implement
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) HasGun() bool {
|
||||||
|
// need to implement
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) HasFamilty() bool {
|
||||||
|
// need to implement
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GamePerson) Type() int {
|
||||||
|
// need to implement
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGamePerson(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, unsafe.Sizeof(GamePerson{}), uintptr(64))
|
||||||
|
|
||||||
|
const x, y, z = math.MinInt32, math.MaxInt32, 0
|
||||||
|
const name = "aaaaaaaaaaaaa_bbbbbbbbbbbbb_cccccccccccccc"
|
||||||
|
const personType = BuilderGamePersonType
|
||||||
|
const gold = math.MaxInt32
|
||||||
|
const mana = 1000
|
||||||
|
const health = 1000
|
||||||
|
const respect = 10
|
||||||
|
const strength = 10
|
||||||
|
const experience = 10
|
||||||
|
const level = 10
|
||||||
|
|
||||||
|
options := []Option{
|
||||||
|
WithName(name),
|
||||||
|
WithCoordinates(x, y, z),
|
||||||
|
WithGold(gold),
|
||||||
|
WithMana(mana),
|
||||||
|
WithHealth(health),
|
||||||
|
WithRespect(respect),
|
||||||
|
WithStrength(strength),
|
||||||
|
WithExperience(experience),
|
||||||
|
WithLevel(level),
|
||||||
|
WithHouse(),
|
||||||
|
WithFamily(),
|
||||||
|
WithType(personType),
|
||||||
|
}
|
||||||
|
|
||||||
|
person := NewGamePerson(options...)
|
||||||
|
assert.Equal(t, name, person.Name())
|
||||||
|
assert.Equal(t, x, person.X())
|
||||||
|
assert.Equal(t, y, person.Y())
|
||||||
|
assert.Equal(t, z, person.Z())
|
||||||
|
assert.Equal(t, gold, person.Gold())
|
||||||
|
assert.Equal(t, mana, person.Mana())
|
||||||
|
assert.Equal(t, health, person.Health())
|
||||||
|
assert.Equal(t, respect, person.Respect())
|
||||||
|
assert.Equal(t, strength, person.Strength())
|
||||||
|
assert.Equal(t, experience, person.Experience())
|
||||||
|
assert.Equal(t, level, person.Level())
|
||||||
|
assert.True(t, person.HasHouse())
|
||||||
|
assert.True(t, person.HasFamilty())
|
||||||
|
assert.False(t, person.HasGun())
|
||||||
|
assert.Equal(t, personType, person.Type())
|
||||||
|
}
|
||||||
123
homework/sync_primitives/homework_test.go
Normal file
123
homework/sync_primitives/homework_test.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RWMutex struct {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RWMutex) Lock() {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RWMutex) Unlock() {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RWMutex) RLock() {
|
||||||
|
// need to implement
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RWMutex) RUnlock() {
|
||||||
|
// need to implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWMutexWithWriter(t *testing.T) {
|
||||||
|
var mutex RWMutex
|
||||||
|
mutex.Lock() // writer
|
||||||
|
|
||||||
|
var mutualExlusionWithWriter atomic.Bool
|
||||||
|
mutualExlusionWithWriter.Store(true)
|
||||||
|
var mutualExlusionWithReader atomic.Bool
|
||||||
|
mutualExlusionWithReader.Store(true)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.Lock() // another writer
|
||||||
|
mutualExlusionWithWriter.Store(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.RLock() // another reader
|
||||||
|
mutualExlusionWithReader.Store(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
assert.True(t, mutualExlusionWithWriter.Load())
|
||||||
|
assert.True(t, mutualExlusionWithReader.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWMutexWithReaders(t *testing.T) {
|
||||||
|
var mutex RWMutex
|
||||||
|
mutex.RLock() // reader
|
||||||
|
|
||||||
|
var mutualExlusionWithWriter atomic.Bool
|
||||||
|
mutualExlusionWithWriter.Store(true)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.Lock() // another writer
|
||||||
|
mutualExlusionWithWriter.Store(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
assert.True(t, mutualExlusionWithWriter.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWMutexMultipleReaders(t *testing.T) {
|
||||||
|
var mutex RWMutex
|
||||||
|
mutex.RLock() // reader
|
||||||
|
|
||||||
|
var readersCount atomic.Int32
|
||||||
|
readersCount.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.RLock() // another reader
|
||||||
|
readersCount.Add(1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.RLock() // another reader
|
||||||
|
readersCount.Add(1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
assert.Equal(t, int32(3), readersCount.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWMutexWithWriterPriority(t *testing.T) {
|
||||||
|
var mutex RWMutex
|
||||||
|
mutex.RLock() // reader
|
||||||
|
|
||||||
|
var mutualExlusionWithWriter atomic.Bool
|
||||||
|
mutualExlusionWithWriter.Store(true)
|
||||||
|
var readersCount atomic.Int32
|
||||||
|
readersCount.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.Lock() // another writer is waiting for reader
|
||||||
|
mutualExlusionWithWriter.Store(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.RLock() // another reader is waiting for a higher priority writer
|
||||||
|
readersCount.Add(1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mutex.RLock() // another reader is waiting for a higher priority writer
|
||||||
|
readersCount.Add(1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
assert.True(t, mutualExlusionWithWriter.Load())
|
||||||
|
assert.Equal(t, int32(1), readersCount.Load())
|
||||||
|
}
|
||||||
19
interviews/data_types/endianness/main.go
Normal file
19
interviews/data_types/endianness/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var number int32 = 0x12345678
|
||||||
|
pointer := unsafe.Pointer(&number)
|
||||||
|
|
||||||
|
fmt.Printf("0x")
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
byteValue := *(*int8)(unsafe.Add(pointer, i))
|
||||||
|
fmt.Printf("%x", byteValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
24
interviews/data_types/endianness_check/main.go
Normal file
24
interviews/data_types/endianness_check/main.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsLittleEndian() bool {
|
||||||
|
var number int16 = 0x0001
|
||||||
|
pointer := (*int8)(unsafe.Pointer(&number))
|
||||||
|
return *pointer == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsBigEndian() bool {
|
||||||
|
return !IsLittleEndian()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if IsLittleEndian() {
|
||||||
|
fmt.Println("Little endian")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Big endian")
|
||||||
|
}
|
||||||
|
}
|
||||||
20
interviews/data_types/int_overflow/main.go
Normal file
20
interviews/data_types/int_overflow/main.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var signed int8 = math.MaxInt8
|
||||||
|
signed++
|
||||||
|
|
||||||
|
var unsigned uint8 = math.MaxUint8
|
||||||
|
unsigned++
|
||||||
|
|
||||||
|
fmt.Println(signed)
|
||||||
|
fmt.Println(unsigned)
|
||||||
|
|
||||||
|
// var signed int8 = math.MaxInt8 + 1 -> compilation error
|
||||||
|
// var unsigned uint8 = math.MaxUint8 + 1 -> compilation error
|
||||||
|
}
|
||||||
11
interviews/data_types/inversion/main.go
Normal file
11
interviews/data_types/inversion/main.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var x uint8 = 3
|
||||||
|
var y int8 = 3
|
||||||
|
|
||||||
|
fmt.Println(^x)
|
||||||
|
fmt.Println(^y)
|
||||||
|
}
|
||||||
8
interviews/data_types/octal_system/main.go
Normal file
8
interviews/data_types/octal_system/main.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
sum := 100 + 010
|
||||||
|
fmt.Println(sum)
|
||||||
|
}
|
||||||
52
interviews/data_types/overflow_detection/main.go
Normal file
52
interviews/data_types/overflow_detection/main.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrIntOverflow = errors.New("integer overflow")
|
||||||
|
|
||||||
|
func Inc(counter int) (int, error) {
|
||||||
|
if counter == math.MaxInt {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add(lhs, rhs int) (int, error) {
|
||||||
|
if rhs > 0 {
|
||||||
|
if lhs > math.MaxInt-rhs {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
}
|
||||||
|
} else if rhs < 0 {
|
||||||
|
if lhs < math.MinInt-rhs {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs + rhs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mul(lhs, rhs int) (int, error) {
|
||||||
|
if lhs == 0 || rhs == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs == 1 || rhs == 1 {
|
||||||
|
return lhs * rhs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs == -1 && rhs == math.MinInt) || (rhs == -1 && lhs == math.MinInt) {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs > math.MaxInt/rhs {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
} else if lhs < math.MinInt/rhs {
|
||||||
|
return 0, ErrIntOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs * rhs, nil
|
||||||
|
}
|
||||||
21
interviews/data_types/pointer_to_pointer_correct/main.go
Normal file
21
interviews/data_types/pointer_to_pointer_correct/main.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func process(temp **int32) {
|
||||||
|
var value2 int32 = 200
|
||||||
|
*temp = &value2
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value1 int32 = 100
|
||||||
|
pointer := &value1
|
||||||
|
|
||||||
|
fmt.Println(*pointer)
|
||||||
|
fmt.Println(pointer)
|
||||||
|
|
||||||
|
process(&pointer)
|
||||||
|
|
||||||
|
fmt.Println(*pointer)
|
||||||
|
fmt.Println(pointer)
|
||||||
|
}
|
||||||
21
interviews/data_types/pointer_to_pointer_incorrect/main.go
Normal file
21
interviews/data_types/pointer_to_pointer_incorrect/main.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func process(temp *int32) {
|
||||||
|
var value2 int32 = 200
|
||||||
|
temp = &value2
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value1 int32 = 100
|
||||||
|
pointer := &value1
|
||||||
|
|
||||||
|
fmt.Println(*pointer)
|
||||||
|
fmt.Println(pointer)
|
||||||
|
|
||||||
|
process(pointer)
|
||||||
|
|
||||||
|
fmt.Println(*pointer)
|
||||||
|
fmt.Println(pointer)
|
||||||
|
}
|
||||||
29
interviews/data_types/uintptr/main.go
Normal file
29
interviews/data_types/uintptr/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go run main.go
|
||||||
|
// go run -gcflags=-d=checkptr main.go
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
x := new(int)
|
||||||
|
y := new(int)
|
||||||
|
z := new(int)
|
||||||
|
|
||||||
|
ptrX := unsafe.Pointer(x)
|
||||||
|
ptrY := unsafe.Pointer(y)
|
||||||
|
addressZ := uintptr(unsafe.Pointer(z))
|
||||||
|
|
||||||
|
// arithmetic operation
|
||||||
|
_ = addressZ + 2
|
||||||
|
_ = addressZ - 2
|
||||||
|
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
*(*int)(ptrX) = 100
|
||||||
|
*(*int)(ptrY) = 200
|
||||||
|
*(*int)(unsafe.Pointer(addressZ)) = 300 // dangerous
|
||||||
|
}
|
||||||
14
lessons/allocator/allocation_1/main.go
Normal file
14
lessons/allocator/allocation_1/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
|
||||||
|
func getResult() int {
|
||||||
|
result := 200
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_ = getResult()
|
||||||
|
}
|
||||||
14
lessons/allocator/allocation_2/main.go
Normal file
14
lessons/allocator/allocation_2/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
|
||||||
|
func getResult() *int {
|
||||||
|
result := 200
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_ = getResult()
|
||||||
|
}
|
||||||
15
lessons/allocator/allocation_3/main.go
Normal file
15
lessons/allocator/allocation_3/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
|
||||||
|
func getResult(number *int) int {
|
||||||
|
result := *number + 200
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
number := 100
|
||||||
|
_ = getResult(&number)
|
||||||
|
}
|
||||||
28
lessons/allocator/allocation_4/main.go
Normal file
28
lessons/allocator/allocation_4/main.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
|
||||||
|
func printValue(v interface{}) {
|
||||||
|
fmt.Println(v)
|
||||||
|
//_, _ = v.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var num1 int = 10
|
||||||
|
var str1 string = "Hello"
|
||||||
|
|
||||||
|
printValue(num1)
|
||||||
|
printValue(str1)
|
||||||
|
|
||||||
|
var num2 int = 10
|
||||||
|
var str2 string = "Hello"
|
||||||
|
|
||||||
|
var i interface{}
|
||||||
|
i = num2
|
||||||
|
i = str2
|
||||||
|
_ = i
|
||||||
|
}
|
||||||
18
lessons/allocator/allocation_5/main.go
Normal file
18
lessons/allocator/allocation_5/main.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
|
||||||
|
func createPointer() *int {
|
||||||
|
value2 := new(int)
|
||||||
|
return value2
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
value1 := new(int) // stack
|
||||||
|
_ = value1
|
||||||
|
|
||||||
|
value2 := createPointer() // heap
|
||||||
|
_ = value2
|
||||||
|
}
|
||||||
33
lessons/allocator/allocation_effect_1/allocation_test.go
Normal file
33
lessons/allocator/allocation_effect_1/allocation_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// go test -bench=. -benchmem
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
iValue int
|
||||||
|
sValue string
|
||||||
|
bValue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func NewDataByValue() Data {
|
||||||
|
return Data{iValue: 100, sValue: "100", bValue: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func NewDataByPointer() *Data {
|
||||||
|
return &Data{iValue: 100, sValue: "100", bValue: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewByValue(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = NewDataByValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewByPointer(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = NewDataByPointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lessons/allocator/allocation_effect_2/allocation_test.go
Normal file
43
lessons/allocator/allocation_effect_2/allocation_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// go test -bench=. -benchmem
|
||||||
|
|
||||||
|
const bufferSize = 10
|
||||||
|
|
||||||
|
type ReaderWithSliceArgument struct{}
|
||||||
|
|
||||||
|
func (r ReaderWithSliceArgument) Read(p []byte) (int, error) {
|
||||||
|
for i := 0; i < bufferSize; i++ {
|
||||||
|
p[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReaderWithSliceReturn struct{}
|
||||||
|
|
||||||
|
func (r ReaderWithSliceReturn) Read(n int) ([]byte, error) {
|
||||||
|
p := make([]byte, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
p[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSliceWithArgument(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
p := make([]byte, bufferSize)
|
||||||
|
reader := ReaderWithSliceArgument{}
|
||||||
|
_, _ = reader.Read(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSliceWithReturn(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
reader := ReaderWithSliceReturn{}
|
||||||
|
_, _ = reader.Read(bufferSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
lessons/allocator/allocation_effect_3/allocation_test.go
Normal file
28
lessons/allocator/allocation_effect_3/allocation_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// go test -bench=. -benchmem
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
pointer *int
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIteration1(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var number int
|
||||||
|
data := &Data{
|
||||||
|
pointer: &number,
|
||||||
|
}
|
||||||
|
_ = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIteration2(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var number int
|
||||||
|
data := &Data{}
|
||||||
|
data.pointer = &number
|
||||||
|
_ = data
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lessons/allocator/allocations_offset/main.go
Normal file
26
lessons/allocator/allocations_offset/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// For memory blocks larger than 32768 bytes,
|
||||||
|
// each of them is always composed of multiple
|
||||||
|
// memory pages. The memory page size used by the
|
||||||
|
// official standard Go runtime (1.22 versions) is 8192 bytes.
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var data1 [32769]byte
|
||||||
|
var data2 [32769]byte
|
||||||
|
|
||||||
|
data1Pointer := unsafe.Pointer(&data1)
|
||||||
|
data2Pointer := unsafe.Pointer(&data2)
|
||||||
|
|
||||||
|
fmt.Println("adress:", data1Pointer, data2Pointer)
|
||||||
|
fmt.Println("size:", unsafe.Sizeof(data1), unsafe.Sizeof(data2))
|
||||||
|
|
||||||
|
distance := uintptr(data2Pointer) - uintptr(data1Pointer)
|
||||||
|
fmt.Println("distance:", distance)
|
||||||
|
fmt.Println("waste:", distance-unsafe.Sizeof(data1))
|
||||||
|
}
|
||||||
24
lessons/allocator/allocations_size/main.go
Normal file
24
lessons/allocator/allocations_size/main.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var result string
|
||||||
|
var buffer []byte = make([]byte, 33)
|
||||||
|
|
||||||
|
func Concat(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
result = string(buffer) + string(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
b := testing.Benchmark(Concat)
|
||||||
|
fmt.Println(b.AllocsPerOp()) // 3
|
||||||
|
fmt.Println(b.AllocedBytesPerOp()) // 176
|
||||||
|
|
||||||
|
// alocated 176 bytes = 48 (not 33) + 48 (not 33) + 80 (not 33 + 33 = 66)
|
||||||
|
// waste = 15 + 15 + 14 = 44 bytes (25% waste)
|
||||||
|
}
|
||||||
30
lessons/allocator/arena_api/main.go
Normal file
30
lessons/allocator/arena_api/main.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// GOEXPERIMENT=arenas go run main.go
|
||||||
|
// go run -tags goexperiment.arenas main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"arena"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
deposit int
|
||||||
|
credit int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := arena.NewArena()
|
||||||
|
defer a.Free()
|
||||||
|
|
||||||
|
value := arena.New[int64](a)
|
||||||
|
_ = value
|
||||||
|
|
||||||
|
data := arena.New[Data](a)
|
||||||
|
_ = data
|
||||||
|
|
||||||
|
slice := arena.MakeSlice[int32](a, 0, 10)
|
||||||
|
_ = slice
|
||||||
|
|
||||||
|
cloned := arena.Clone[*Data](data) // moved to heap
|
||||||
|
_ = cloned
|
||||||
|
}
|
||||||
24
lessons/allocator/arena_problem_1/main.go
Normal file
24
lessons/allocator/arena_problem_1/main.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// GOEXPERIMENT=arenas go run main.go
|
||||||
|
// go run -tags goexperiment.arenas main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"arena"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
value int
|
||||||
|
operaions []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := arena.NewArena()
|
||||||
|
defer a.Free()
|
||||||
|
|
||||||
|
// Arenas will not allocate all reference types automatically
|
||||||
|
operations := arena.MakeSlice[int](a, 0, 100)
|
||||||
|
data := arena.New[Data](a)
|
||||||
|
data.operaions = operations
|
||||||
|
_ = data
|
||||||
|
}
|
||||||
18
lessons/allocator/arena_problem_2/main.go
Normal file
18
lessons/allocator/arena_problem_2/main.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// GOEXPERIMENT=arenas go run main.go
|
||||||
|
// go run -tags goexperiment.arenas main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"arena"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mem := arena.NewArena()
|
||||||
|
defer mem.Free()
|
||||||
|
|
||||||
|
slice := arena.NewSlice[int](mem, 0, 5)
|
||||||
|
slice = append(slice, 1, 2, 3, 4, 5)
|
||||||
|
|
||||||
|
slice = append(slice, 6) // moved to heap
|
||||||
|
}
|
||||||
23
lessons/allocator/arena_problem_3/main.go
Normal file
23
lessons/allocator/arena_problem_3/main.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// GOEXPERIMENT=arenas go run main.go
|
||||||
|
// go run -tags goexperiment.arenas main.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"arena"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
deposit int
|
||||||
|
credit int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := arena.NewArena()
|
||||||
|
data := arena.New[Data](a)
|
||||||
|
a.Free()
|
||||||
|
|
||||||
|
// use after free
|
||||||
|
fmt.Println(data)
|
||||||
|
}
|
||||||
22
lessons/allocator/big_allocations/main.go
Normal file
22
lessons/allocator/big_allocations/main.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// go run main.go
|
||||||
|
// GOGC=off go run main.go
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
data = make([]byte, 1<<30)
|
||||||
|
/*for idx := 0; idx < 1<<30; idx += 4096 {
|
||||||
|
data[idx] = 100
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fmt.Println("allocated GB:", count)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
74
lessons/allocator/linear_allocator/main.go
Normal file
74
lessons/allocator/linear_allocator/main.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinearAllocator struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinearAllocator(capacity int) (LinearAllocator, error) {
|
||||||
|
if capacity <= 0 {
|
||||||
|
return LinearAllocator{}, errors.New("incorrect capacity")
|
||||||
|
}
|
||||||
|
|
||||||
|
return LinearAllocator{
|
||||||
|
data: make([]byte, 0, capacity),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LinearAllocator) Allocate(size int) (unsafe.Pointer, error) {
|
||||||
|
previousLength := len(a.data)
|
||||||
|
newLength := previousLength + size
|
||||||
|
|
||||||
|
if newLength > cap(a.data) {
|
||||||
|
// can increase capacity
|
||||||
|
return nil, errors.New("not enough memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data = a.data[:newLength]
|
||||||
|
pointer := unsafe.Pointer(&a.data[previousLength])
|
||||||
|
return pointer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// not supported by this kind of allocator
|
||||||
|
// func (a *LinearAllocator) Deallocate(pointer unsafe.Pointer) error {}
|
||||||
|
|
||||||
|
func (a *LinearAllocator) Free() {
|
||||||
|
a.data = a.data[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func store[T any](pointer unsafe.Pointer, value T) {
|
||||||
|
*(*T)(pointer) = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func load[T any](pointer unsafe.Pointer) T {
|
||||||
|
return *(*T)(pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const MB = 1 << 20
|
||||||
|
allocator, err := NewLinearAllocator(MB)
|
||||||
|
if err != nil {
|
||||||
|
// handling...
|
||||||
|
}
|
||||||
|
|
||||||
|
defer allocator.Free()
|
||||||
|
|
||||||
|
pointer1, _ := allocator.Allocate(2)
|
||||||
|
pointer2, _ := allocator.Allocate(4)
|
||||||
|
|
||||||
|
store[int16](pointer1, 100)
|
||||||
|
store[int32](pointer2, 200)
|
||||||
|
|
||||||
|
value1 := load[int16](pointer1)
|
||||||
|
value2 := load[int32](pointer2)
|
||||||
|
fmt.Println("value1:", value1)
|
||||||
|
fmt.Println("value2:", value2)
|
||||||
|
|
||||||
|
fmt.Println("address1:", pointer1)
|
||||||
|
fmt.Println("address2:", pointer2)
|
||||||
|
}
|
||||||
26
lessons/allocator/loop_allocations/allocations_test.go
Normal file
26
lessons/allocator/loop_allocations/allocations_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// go test -bench=. allocations_test.go -benchmem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func Initialize(value *int) {
|
||||||
|
*value = 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWithoutLoopAllocation(b *testing.B) {
|
||||||
|
var value int
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Initialize(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWithLoopAllocation(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var value int
|
||||||
|
Initialize(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
lessons/allocator/pool/pool_test.go
Normal file
50
lessons/allocator/pool/pool_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// go test -bench=. pool_test.go -benchmem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonsPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPersonsPool() *PersonsPool {
|
||||||
|
return &PersonsPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} { return new(Person) },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *PersonsPool) Get() *Person {
|
||||||
|
return p.pool.Get().(*Person)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PersonsPool) Put(person *Person) {
|
||||||
|
p.pool.Put(person)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gPerson *Person
|
||||||
|
|
||||||
|
func BenchmarkWithPool(b *testing.B) {
|
||||||
|
pool := NewPersonsPool()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
person := pool.Get()
|
||||||
|
person.name = "Ivan" // need to reset values
|
||||||
|
gPerson = person
|
||||||
|
pool.Put(person)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWithoutPool(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
person := &Person{name: "Ivan"}
|
||||||
|
gPerson = person
|
||||||
|
}
|
||||||
|
}
|
||||||
100
lessons/allocator/pool_allocator/main.go
Normal file
100
lessons/allocator/pool_allocator/main.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PoolAllocator struct {
|
||||||
|
objectPool []byte
|
||||||
|
freeObjects map[unsafe.Pointer]struct{}
|
||||||
|
objectSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPoolAllocator(capacity int, objectSize int) (PoolAllocator, error) {
|
||||||
|
if capacity <= 0 || objectSize <= 0 || capacity%objectSize != 0 {
|
||||||
|
return PoolAllocator{}, errors.New("incorrect argumnets")
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator := PoolAllocator{
|
||||||
|
objectPool: make([]byte, capacity),
|
||||||
|
freeObjects: make(map[unsafe.Pointer]struct{}, capacity/objectSize),
|
||||||
|
objectSize: objectSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator.resetMemoryState()
|
||||||
|
return allocator, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PoolAllocator) Allocate() (unsafe.Pointer, error) {
|
||||||
|
if len(a.freeObjects) == 0 {
|
||||||
|
// can increase capacity
|
||||||
|
return nil, errors.New("not enough memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pointer unsafe.Pointer
|
||||||
|
for freePointer := range a.freeObjects {
|
||||||
|
pointer = freePointer
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.freeObjects, pointer)
|
||||||
|
return pointer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PoolAllocator) Deallocate(pointer unsafe.Pointer) error {
|
||||||
|
if pointer == nil {
|
||||||
|
return errors.New("incorrect pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// potentionally incorrect pointer
|
||||||
|
a.freeObjects[pointer] = struct{}{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PoolAllocator) Free() {
|
||||||
|
a.resetMemoryState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PoolAllocator) resetMemoryState() {
|
||||||
|
for offset := 0; offset < len(a.objectPool); offset += a.objectSize {
|
||||||
|
pointer := unsafe.Pointer(&a.objectPool[offset])
|
||||||
|
a.freeObjects[pointer] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func store[T any](pointer unsafe.Pointer, value T) {
|
||||||
|
*(*T)(pointer) = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func load[T any](pointer unsafe.Pointer) T {
|
||||||
|
return *(*T)(pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const KB = 1 << 10
|
||||||
|
allocator, err := NewPoolAllocator(KB, 4)
|
||||||
|
if err != nil {
|
||||||
|
// handling...
|
||||||
|
}
|
||||||
|
|
||||||
|
defer allocator.Free()
|
||||||
|
|
||||||
|
pointer1, _ := allocator.Allocate()
|
||||||
|
pointer2, _ := allocator.Allocate()
|
||||||
|
|
||||||
|
store[int32](pointer1, 100)
|
||||||
|
store[int32](pointer2, 200)
|
||||||
|
|
||||||
|
value1 := load[int32](pointer1)
|
||||||
|
value2 := load[int32](pointer2)
|
||||||
|
fmt.Println("value1:", value1)
|
||||||
|
fmt.Println("value2:", value2)
|
||||||
|
|
||||||
|
fmt.Println("address1:", pointer1)
|
||||||
|
fmt.Println("address2:", pointer2)
|
||||||
|
|
||||||
|
allocator.Deallocate(pointer1)
|
||||||
|
allocator.Deallocate(pointer2)
|
||||||
|
}
|
||||||
100
lessons/allocator/stack_allocator/main.go
Normal file
100
lessons/allocator/stack_allocator/main.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const headerSize = 2
|
||||||
|
|
||||||
|
type StackAllocator struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStackAllocator(capacity int) (StackAllocator, error) {
|
||||||
|
if capacity <= 0 {
|
||||||
|
return StackAllocator{}, errors.New("incorrect capacity")
|
||||||
|
}
|
||||||
|
|
||||||
|
return StackAllocator{
|
||||||
|
data: make([]byte, 0, capacity),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *StackAllocator) Allocate(size int) (unsafe.Pointer, error) {
|
||||||
|
if size > math.MaxInt16 {
|
||||||
|
// can increase header size
|
||||||
|
return nil, errors.New("incorrect size")
|
||||||
|
}
|
||||||
|
|
||||||
|
previousLength := len(a.data)
|
||||||
|
newLength := previousLength + headerSize + size
|
||||||
|
|
||||||
|
if newLength > cap(a.data) {
|
||||||
|
// can increase capacity
|
||||||
|
return nil, errors.New("not enough memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data = a.data[:newLength]
|
||||||
|
header := unsafe.Pointer(&a.data[previousLength])
|
||||||
|
pointer := unsafe.Pointer(&a.data[previousLength+headerSize])
|
||||||
|
|
||||||
|
*(*int16)(header) = int16(size)
|
||||||
|
return pointer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *StackAllocator) Deallocate(pointer unsafe.Pointer) error {
|
||||||
|
// can deallocate without pointer
|
||||||
|
if pointer == nil {
|
||||||
|
return errors.New("incorrect pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
header := unsafe.Add(pointer, -headerSize)
|
||||||
|
size := *(*int16)(header)
|
||||||
|
|
||||||
|
previousLength := len(a.data)
|
||||||
|
newLength := previousLength - headerSize - int(size)
|
||||||
|
|
||||||
|
a.data = a.data[:newLength]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *StackAllocator) Free() {
|
||||||
|
a.data = a.data[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func store[T any](pointer unsafe.Pointer, value T) {
|
||||||
|
*(*T)(pointer) = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func load[T any](pointer unsafe.Pointer) T {
|
||||||
|
return *(*T)(pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const KB = 1 << 10
|
||||||
|
allocator, err := NewStackAllocator(KB)
|
||||||
|
if err != nil {
|
||||||
|
// handling...
|
||||||
|
}
|
||||||
|
|
||||||
|
defer allocator.Free()
|
||||||
|
|
||||||
|
pointer1, _ := allocator.Allocate(2)
|
||||||
|
defer allocator.Deallocate(pointer1)
|
||||||
|
pointer2, _ := allocator.Allocate(4)
|
||||||
|
defer allocator.Deallocate(pointer2)
|
||||||
|
|
||||||
|
store[int16](pointer1, 100)
|
||||||
|
store[int32](pointer2, 200)
|
||||||
|
|
||||||
|
value1 := load[int16](pointer1)
|
||||||
|
value2 := load[int32](pointer2)
|
||||||
|
fmt.Println("value1:", value1)
|
||||||
|
fmt.Println("value2:", value2)
|
||||||
|
|
||||||
|
fmt.Println("address1:", pointer1)
|
||||||
|
fmt.Println("address2:", pointer2)
|
||||||
|
}
|
||||||
20
lessons/allocator/stack_growth/main.go
Normal file
20
lessons/allocator/stack_growth/main.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// -l = disable inlining
|
||||||
|
// -m = print optimization decisions
|
||||||
|
// go build -gcflags '-l -m'
|
||||||
|
// go run -gcflags '-l' main.go
|
||||||
|
|
||||||
|
func process(index int) byte {
|
||||||
|
var data [1 << 20]byte
|
||||||
|
return data[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var index int = 100
|
||||||
|
|
||||||
|
pointer := &index
|
||||||
|
println("pointer:", pointer)
|
||||||
|
process(index)
|
||||||
|
println("pointer:", pointer)
|
||||||
|
}
|
||||||
38
lessons/channels/broadcast/main.go
Normal file
38
lessons/channels/broadcast/main.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func notifier(signals chan int) {
|
||||||
|
close(signals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscriber(signals chan int) {
|
||||||
|
<-signals
|
||||||
|
fmt.Println("signaled")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
signals := make(chan int)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(3)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
notifier(signals)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
subscriber(signals)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
subscriber(signals)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
7
lessons/channels/buffered_channel/main.go
Normal file
7
lessons/channels/buffered_channel/main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int, 2)
|
||||||
|
ch <- 100
|
||||||
|
ch <- 100
|
||||||
|
}
|
||||||
21
lessons/channels/channel_data_race/main.go
Normal file
21
lessons/channels/channel_data_race/main.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// go run -race main.go
|
||||||
|
|
||||||
|
var buffer chan int
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(100)
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
buffer = make(chan int)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
21
lessons/channels/channel_interaction/main.go
Normal file
21
lessons/channels/channel_interaction/main.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Describe blocking
|
||||||
|
|
||||||
|
func async(ch chan string) {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
ch <- "async result"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan string)
|
||||||
|
go async(ch)
|
||||||
|
// ...
|
||||||
|
result := <-ch
|
||||||
|
fmt.Println(result)
|
||||||
|
}
|
||||||
14
lessons/channels/copy_channel/main.go
Normal file
14
lessons/channels/copy_channel/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
source := make(chan int)
|
||||||
|
clone := source
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
source <- 1
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println(<-clone)
|
||||||
|
}
|
||||||
46
lessons/channels/deadlock/main.go
Normal file
46
lessons/channels/deadlock/main.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var actions int
|
||||||
|
var mutex sync.Mutex
|
||||||
|
var buffer chan struct{}
|
||||||
|
|
||||||
|
func consumer() {
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
mutex.Lock()
|
||||||
|
actions++
|
||||||
|
<-buffer
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func producer() {
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
buffer <- struct{}{}
|
||||||
|
mutex.Lock()
|
||||||
|
actions++
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
buffer = make(chan struct{}, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
consumer()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
producer()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
26
lessons/channels/goroutine_leak_1/main.go
Normal file
26
lessons/channels/goroutine_leak_1/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
doWork := func(strings <-chan string) {
|
||||||
|
go func() {
|
||||||
|
for str := range strings {
|
||||||
|
fmt.Println(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("doWork exited")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
strings := make(chan string)
|
||||||
|
doWork(strings)
|
||||||
|
strings <- "Test"
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
fmt.Println("Done")
|
||||||
|
}
|
||||||
15
lessons/channels/goroutine_leak_2/main.go
Normal file
15
lessons/channels/goroutine_leak_2/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Need to show solution and describe close
|
||||||
|
|
||||||
|
// First-response-wins strategy
|
||||||
|
func request() int {
|
||||||
|
ch := make(chan int)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go func() {
|
||||||
|
ch <- i // 4 goroutines will be blocked
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return <-ch
|
||||||
|
}
|
||||||
22
lessons/channels/increment_with_channel/main.go
Normal file
22
lessons/channels/increment_with_channel/main.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ch <- 1
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
ch <- 1
|
||||||
|
}()
|
||||||
|
|
||||||
|
value := 0
|
||||||
|
value += <-ch
|
||||||
|
value += <-ch
|
||||||
|
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
27
lessons/channels/increment_with_mutex/main.go
Normal file
27
lessons/channels/increment_with_mutex/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mutex := sync.Mutex{}
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
value := 0
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
value++
|
||||||
|
mutex.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
19
lessons/channels/is_closed_1/main.go
Normal file
19
lessons/channels/is_closed_1/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func IsClosed(ch chan int) bool {
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int)
|
||||||
|
fmt.Println(IsClosed(ch))
|
||||||
|
close(ch)
|
||||||
|
fmt.Println(IsClosed(ch))
|
||||||
|
}
|
||||||
22
lessons/channels/is_closed_2/main.go
Normal file
22
lessons/channels/is_closed_2/main.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Need to show solution
|
||||||
|
|
||||||
|
func IsClosed(ch chan int) bool {
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int, 1)
|
||||||
|
ch <- 1
|
||||||
|
fmt.Println(IsClosed(ch))
|
||||||
|
close(ch)
|
||||||
|
fmt.Println(IsClosed(ch))
|
||||||
|
}
|
||||||
47
lessons/channels/nil_channel/main.go
Normal file
47
lessons/channels/nil_channel/main.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Need to show solution
|
||||||
|
|
||||||
|
func WaitToClose(lhs, rhs chan struct{}) {
|
||||||
|
lhsClosed, rhsClosed := false, false
|
||||||
|
for !lhsClosed || !rhsClosed {
|
||||||
|
select {
|
||||||
|
case _, ok := <-lhs:
|
||||||
|
fmt.Println("lhs", ok)
|
||||||
|
if !ok {
|
||||||
|
lhsClosed = true
|
||||||
|
}
|
||||||
|
case _, ok := <-rhs:
|
||||||
|
fmt.Println("rhs", ok)
|
||||||
|
if !ok {
|
||||||
|
rhsClosed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
lhs := make(chan struct{}, 1)
|
||||||
|
rhs := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
WaitToClose(lhs, rhs)
|
||||||
|
}()
|
||||||
|
|
||||||
|
lhs <- struct{}{}
|
||||||
|
rhs <- struct{}{}
|
||||||
|
|
||||||
|
close(lhs)
|
||||||
|
close(rhs)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
19
lessons/channels/nil_channel_task/main.go
Normal file
19
lessons/channels/nil_channel_task/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int, 1)
|
||||||
|
for done := false; !done; {
|
||||||
|
select {
|
||||||
|
default:
|
||||||
|
fmt.Println(3)
|
||||||
|
done = true
|
||||||
|
case <-ch:
|
||||||
|
fmt.Println(2)
|
||||||
|
ch = nil
|
||||||
|
case ch <- 1:
|
||||||
|
fmt.Println(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
lessons/channels/non_blocking_channels_correct/main.go
Normal file
27
lessons/channels/non_blocking_channels_correct/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func tryToReadFromChannel(ch chan string) (string, bool) {
|
||||||
|
select {
|
||||||
|
case value := <-ch:
|
||||||
|
return value, true
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryToWriteToChannel(ch chan string, value string) bool {
|
||||||
|
select {
|
||||||
|
case ch <- value:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryToReadOrWrite(ch1 chan string, ch2 chan string) {
|
||||||
|
select {
|
||||||
|
case <-ch1:
|
||||||
|
case ch2 <- "test":
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lessons/channels/non_blocking_channels_incorrect/main.go
Normal file
19
lessons/channels/non_blocking_channels_incorrect/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func tryToReadFromChannel(ch chan string) (string, bool) {
|
||||||
|
if len(ch) != 0 {
|
||||||
|
value := <-ch
|
||||||
|
return value, true
|
||||||
|
} else {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryToWriteToChannel(ch chan string, value string) bool {
|
||||||
|
if len(ch) < cap(ch) {
|
||||||
|
ch <- value
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
91
lessons/channels/operations_with_channel/main.go
Normal file
91
lessons/channels/operations_with_channel/main.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeToNilChannel() {
|
||||||
|
var ch chan int
|
||||||
|
ch <- 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeToClosedChannel() {
|
||||||
|
ch := make(chan int, 2)
|
||||||
|
close(ch)
|
||||||
|
ch <- 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// Descibe read after close
|
||||||
|
|
||||||
|
func readFromChannel() {
|
||||||
|
ch := make(chan int, 2)
|
||||||
|
ch <- 10
|
||||||
|
ch <- 20
|
||||||
|
|
||||||
|
val, ok := <-ch
|
||||||
|
fmt.Println(val, ok)
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
val, ok = <-ch
|
||||||
|
fmt.Println(val, ok)
|
||||||
|
|
||||||
|
val, ok = <-ch
|
||||||
|
fmt.Println(val, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAnyChannels() {
|
||||||
|
ch1 := make(chan int)
|
||||||
|
ch2 := make(chan int)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ch1 <- 100
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ch2 <- 200
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case val1 := <-ch1:
|
||||||
|
fmt.Println(val1)
|
||||||
|
case val2 := <-ch2:
|
||||||
|
fmt.Println(val2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFromNilChannel() {
|
||||||
|
var ch chan int
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func rangeNilChannel() {
|
||||||
|
var ch chan int
|
||||||
|
for range ch {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeNilChannel() {
|
||||||
|
var ch chan int
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeChannelAnyTimes() {
|
||||||
|
ch := make(chan int)
|
||||||
|
close(ch)
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareChannels() {
|
||||||
|
ch1 := make(chan int)
|
||||||
|
ch2 := make(chan int)
|
||||||
|
|
||||||
|
equal1 := ch1 == ch2
|
||||||
|
equal2 := ch1 == ch1
|
||||||
|
|
||||||
|
fmt.Println(equal1)
|
||||||
|
fmt.Println(equal2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
33
lessons/channels/prioritization/main.go
Normal file
33
lessons/channels/prioritization/main.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Need to show solution
|
||||||
|
|
||||||
|
func producer(ch chan<- int) {
|
||||||
|
for {
|
||||||
|
ch <- 1
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch1 := make(chan int) // more prioritized
|
||||||
|
ch2 := make(chan int)
|
||||||
|
|
||||||
|
go producer(ch1)
|
||||||
|
go producer(ch2)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case value := <-ch1:
|
||||||
|
fmt.Println(value)
|
||||||
|
return
|
||||||
|
case value := <-ch2:
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
lessons/channels/prioritization_weight/main.go
Normal file
27
lessons/channels/prioritization_weight/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch1 := make(chan struct{}, 1)
|
||||||
|
ch2 := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
close(ch1)
|
||||||
|
close(ch2)
|
||||||
|
|
||||||
|
ch1Value := 0.0
|
||||||
|
ch2Value := 0.0
|
||||||
|
|
||||||
|
for i := 0; i < 100000; i++ {
|
||||||
|
select {
|
||||||
|
case <-ch1:
|
||||||
|
ch1Value++
|
||||||
|
case <-ch1:
|
||||||
|
ch1Value++
|
||||||
|
case <-ch2:
|
||||||
|
ch2Value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(ch1Value / ch2Value)
|
||||||
|
}
|
||||||
51
lessons/channels/producer_consumer/main.go
Normal file
51
lessons/channels/producer_consumer/main.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func producer(ch chan int) {
|
||||||
|
defer close(ch)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
ch <- i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumer(ch chan int) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case value, opened := <-ch:
|
||||||
|
if !opened {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
for value := range ch { // syntax sugar
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
producer(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
consumer(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
36
lessons/channels/select/main.go
Normal file
36
lessons/channels/select/main.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func async1() chan string {
|
||||||
|
ch := make(chan string)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
ch <- "async1 result"
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func async2() chan string {
|
||||||
|
ch := make(chan string)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
ch <- "async2 result"
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch1 := async1()
|
||||||
|
ch2 := async2()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case result := <-ch1:
|
||||||
|
fmt.Println(result)
|
||||||
|
case result := <-ch2:
|
||||||
|
fmt.Println(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lessons/channels/select_forever/main.go
Normal file
29
lessons/channels/select_forever/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func way1() {
|
||||||
|
make(chan struct{}) <- struct{}{}
|
||||||
|
// or
|
||||||
|
make(chan<- struct{}) <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func way2() {
|
||||||
|
<-make(chan struct{})
|
||||||
|
// or
|
||||||
|
<-make(<-chan struct{})
|
||||||
|
// or
|
||||||
|
for range make(<-chan struct{}) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func way3() {
|
||||||
|
chan struct{}(nil) <- struct{}{}
|
||||||
|
// or
|
||||||
|
<-chan struct{}(nil)
|
||||||
|
// or
|
||||||
|
for range chan struct{}(nil) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func way4() {
|
||||||
|
select {}
|
||||||
|
}
|
||||||
33
lessons/channels/select_with_break_and_continue/main.go
Normal file
33
lessons/channels/select_with_break_and_continue/main.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := make(chan int)
|
||||||
|
go func() {
|
||||||
|
for i := 1; i <= 4; i++ {
|
||||||
|
data <- i
|
||||||
|
}
|
||||||
|
close(data)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
value := 0
|
||||||
|
opened := true
|
||||||
|
|
||||||
|
select {
|
||||||
|
case value, opened = <-data:
|
||||||
|
if value == 2 {
|
||||||
|
continue
|
||||||
|
} else if value == 3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opened {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
15
lessons/channels/select_with_main_goroutine/main.go
Normal file
15
lessons/channels/select_with_main_goroutine/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
func doSomething() {
|
||||||
|
for {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
go doSomething()
|
||||||
|
go doSomething()
|
||||||
|
select {}
|
||||||
|
}
|
||||||
38
lessons/channels/sequential_execution/main.go
Normal file
38
lessons/channels/sequential_execution/main.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Need to show solution
|
||||||
|
|
||||||
|
func FetchData1() chan int {
|
||||||
|
ch := make(chan int)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
ch <- 10
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchData2() chan int {
|
||||||
|
ch := make(chan int)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
ch <- 20
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func Process(value1, value2 int) {
|
||||||
|
// Processing...
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
start := time.Now()
|
||||||
|
Process(<-FetchData1(), <-FetchData2())
|
||||||
|
fmt.Println(time.Now().Sub(start))
|
||||||
|
}
|
||||||
33
lessons/channels/signals/main.go
Normal file
33
lessons/channels/signals/main.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func notifier(signals chan struct{}) {
|
||||||
|
signals <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscriber(signals chan struct{}) {
|
||||||
|
<-signals
|
||||||
|
fmt.Println("signaled")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
signals := make(chan struct{})
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
notifier(signals)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
subscriber(signals)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
19
lessons/channels/unidirectional_channels/main.go
Normal file
19
lessons/channels/unidirectional_channels/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func in(in chan<- int) {
|
||||||
|
in <- 100
|
||||||
|
close(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func out(out <-chan int) {
|
||||||
|
fmt.Println(<-out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var ch = make(chan int, 1)
|
||||||
|
|
||||||
|
in(ch)
|
||||||
|
out(ch)
|
||||||
|
}
|
||||||
19
lessons/channels/write_after_close/main.go
Normal file
19
lessons/channels/write_after_close/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan int)
|
||||||
|
go func() {
|
||||||
|
ch <- 1
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
<-ch
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
29
lessons/contexts/after_done/main.go
Normal file
29
lessons/contexts/after_done/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// context.AfterFunc
|
||||||
|
|
||||||
|
func WithCtxAfterFunc(ctx context.Context, action func()) {
|
||||||
|
if action != nil {
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
action()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
WithCtxAfterFunc(ctx, func() {
|
||||||
|
fmt.Println("after")
|
||||||
|
})
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
17
lessons/contexts/chech_cancel/main.go
Normal file
17
lessons/contexts/chech_cancel/main.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func incorrectCheck(ctx context.Context, stream <-chan string) {
|
||||||
|
data := <-stream
|
||||||
|
_ = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func correctCheck(ctx context.Context, stream <-chan string) {
|
||||||
|
select {
|
||||||
|
case data := <-stream:
|
||||||
|
_ = data
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lessons/contexts/context_handling/main.go
Normal file
26
lessons/contexts/context_handling/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Query(string) string
|
||||||
|
|
||||||
|
func DoQeury(qyeryStr string) (string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var resultCh chan string
|
||||||
|
go func() {
|
||||||
|
result := Query(qyeryStr)
|
||||||
|
resultCh <- result
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return "", ctx.Err()
|
||||||
|
case result := <-resultCh:
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lessons/contexts/context_inheritance_1/main.go
Normal file
29
lessons/contexts/context_inheritance_1/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
makeRequest(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRequest(ctx context.Context) {
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-newCtx.Done():
|
||||||
|
fmt.Println("canceled")
|
||||||
|
case <-timer.C:
|
||||||
|
fmt.Println("timer")
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lessons/contexts/context_inheritance_2/main.go
Normal file
19
lessons/contexts/context_inheritance_2/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, cancel = context.WithCancel(ctx)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
fmt.Println("canceled")
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lessons/contexts/context_with_cancel/main.go
Normal file
44
lessons/contexts/context_with_cancel/main.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func receiveWeather(ctx context.Context, result chan struct{}, idx int) {
|
||||||
|
randomTime := time.Duration(rand.Intn(5000)) * time.Millisecond
|
||||||
|
|
||||||
|
timer := time.NewTimer(randomTime)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
fmt.Printf("finished: %d\n", idx)
|
||||||
|
result <- struct{}{}
|
||||||
|
case <-ctx.Done():
|
||||||
|
fmt.Printf("canceled: %d\n", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(10)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
result := make(chan struct{}, 10)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func(idx int) {
|
||||||
|
defer wg.Done()
|
||||||
|
receiveWeather(ctx, result, idx)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-result
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
15
lessons/contexts/context_with_cancel_cause/main.go
Normal file
15
lessons/contexts/context_with_cancel_cause/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancelCause(context.Background())
|
||||||
|
cancel(errors.New("error"))
|
||||||
|
|
||||||
|
fmt.Println(ctx.Err())
|
||||||
|
fmt.Println(context.Cause(ctx))
|
||||||
|
}
|
||||||
22
lessons/contexts/context_with_http_client/main.go
Normal file
22
lessons/contexts/context_with_http_client/main.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://example.com", nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = http.DefaultClient.Do(req); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
34
lessons/contexts/context_with_http_server/main.go
Normal file
34
lessons/contexts/context_with_http_server/main.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
helloWorldHandler := http.HandlerFunc(handle)
|
||||||
|
http.Handle("/welcome", injectTraceID(helloWorldHandler))
|
||||||
|
_ = http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(_ http.ResponseWriter, r *http.Request) {
|
||||||
|
value, ok := r.Context().Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeRequest(r.Context())
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRequest(_ context.Context) {
|
||||||
|
// requesting to database with context
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectTraceID(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), "trace_id", "12-21-33")
|
||||||
|
req := r.WithContext(ctx)
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
26
lessons/contexts/context_with_timeout/main.go
Normal file
26
lessons/contexts/context_with_timeout/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeRequest(ctx context.Context) {
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
fmt.Println("finished")
|
||||||
|
case <-ctx.Done():
|
||||||
|
fmt.Println("canceled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
makeRequest(ctx)
|
||||||
|
}
|
||||||
18
lessons/contexts/context_with_timeout_cause/main.go
Normal file
18
lessons/contexts/context_with_timeout_cause/main.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeoutCause(context.Background(), time.Second, errors.New("timeout"))
|
||||||
|
defer cancel() // show difference
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
fmt.Println(ctx.Err())
|
||||||
|
fmt.Println(context.Cause(ctx))
|
||||||
|
}
|
||||||
81
lessons/contexts/context_with_timeout_implementation/main.go
Normal file
81
lessons/contexts/context_with_timeout_implementation/main.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
done chan struct{}
|
||||||
|
closed int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTimeout(parent Context, duration time.Duration) (*Context, func()) {
|
||||||
|
if atomic.LoadInt32(&parent.closed) == 1 {
|
||||||
|
return nil, nil // don't use nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &Context{
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel := func() {
|
||||||
|
if atomic.CompareAndSwapInt32(&ctx.closed, 0, 1) {
|
||||||
|
close(ctx.done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
timer := time.NewTimer(duration)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-parent.Done():
|
||||||
|
case <-timer.C:
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Done() <-chan struct{} {
|
||||||
|
return c.done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Err() error {
|
||||||
|
select {
|
||||||
|
case <-c.done:
|
||||||
|
return errors.New("context deadline exceeded")
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Deadline() (time.Time, bool) {
|
||||||
|
// not implemented
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Value(any) any {
|
||||||
|
// not implemented
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := WithTimeout(Context{}, time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
timer := time.NewTimer(5 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
fmt.Println("finished")
|
||||||
|
case <-ctx.Done():
|
||||||
|
fmt.Println("canceled")
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lessons/contexts/context_with_value/main.go
Normal file
29
lessons/contexts/context_with_value/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
traceCtx := context.WithValue(context.Background(), "trace_id", "12-21-33")
|
||||||
|
makeRequest(traceCtx)
|
||||||
|
|
||||||
|
oldValue, ok := traceCtx.Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println("mainValue", oldValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRequest(ctx context.Context) {
|
||||||
|
oldValue, ok := ctx.Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println("oldValue", oldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCtx := context.WithValue(ctx, "trace_id", "22-22-22")
|
||||||
|
newValue, ok := newCtx.Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println("newValue", newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lessons/contexts/context_with_value_inheritance/main.go
Normal file
26
lessons/contexts/context_with_value_inheritance/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
traceCtx := context.WithValue(context.Background(), "trace_id", "12-21-33")
|
||||||
|
makeRequest(traceCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRequest(ctx context.Context) {
|
||||||
|
oldValue, ok := ctx.Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println(oldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCtx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
newValue, ok := newCtx.Value("trace_id").(string)
|
||||||
|
if ok {
|
||||||
|
fmt.Println(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
27
lessons/contexts/context_with_value_type/main.go
Normal file
27
lessons/contexts/context_with_value_type/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
{
|
||||||
|
ctx := context.WithValue(context.Background(), "key", "value1")
|
||||||
|
ctx = context.WithValue(ctx, "key", "value2")
|
||||||
|
|
||||||
|
fmt.Println("string =", ctx.Value("key").(string))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
type key1 string // type definition, not type alias
|
||||||
|
type key2 string // type definition, not type alias
|
||||||
|
const k1 key1 = "key"
|
||||||
|
const k2 key2 = "key"
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), k1, "value1")
|
||||||
|
ctx = context.WithValue(ctx, k2, "value2")
|
||||||
|
|
||||||
|
fmt.Println("key1 =", ctx.Value(k1).(string))
|
||||||
|
fmt.Println("key2 =", ctx.Value(k2).(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lessons/contexts/context_without_cancel/main.go
Normal file
17
lessons/contexts/context_without_cancel/main.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
innerCtx := context.WithoutCancel(ctx)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if innerCtx.Err() != nil {
|
||||||
|
fmt.Println("canceled")
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lessons/contexts/errgroup_with_ctx/go.mod
Normal file
5
lessons/contexts/errgroup_with_ctx/go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module errgroup
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require golang.org/x/sync v0.6.0
|
||||||
2
lessons/contexts/errgroup_with_ctx/go.sum
Normal file
2
lessons/contexts/errgroup_with_ctx/go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
39
lessons/contexts/errgroup_with_ctx/main.go
Normal file
39
lessons/contexts/errgroup_with_ctx/main.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
group, groupCtx := errgroup.WithContext(ctx)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
group.Go(func() error {
|
||||||
|
timeout := time.Second * time.Duration(rand.Intn(10))
|
||||||
|
|
||||||
|
timer := time.NewTimer(timeout)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
fmt.Println("timeout")
|
||||||
|
return errors.New("error")
|
||||||
|
case <-groupCtx.Done():
|
||||||
|
fmt.Println("canceled")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := group.Wait(); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lessons/contexts/graceful_shutdown/main.go
Normal file
43
lessons/contexts/graceful_shutdown/main.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = io.WriteString(w, "hello world\n")
|
||||||
|
})
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":8888",
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := server.ListenAndServe()
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Print(err.Error()) // exit
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err := server.Shutdown(ctx); err != nil {
|
||||||
|
log.Print(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("canceled")
|
||||||
|
}
|
||||||
15
lessons/contexts/nil_context/main.go
Normal file
15
lessons/contexts/nil_context/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func process(ctx context.Context) {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
// handling...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
process(nil)
|
||||||
|
}
|
||||||
18
lessons/contexts/with_ctx_check/main.go
Normal file
18
lessons/contexts/with_ctx_check/main.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func WithContexCheck(ctx context.Context, action func()) {
|
||||||
|
if action == nil || ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
WithContexCheck(ctx, func() {
|
||||||
|
// do something
|
||||||
|
})
|
||||||
|
}
|
||||||
36
lessons/data_types/bit_mask/main.go
Normal file
36
lessons/data_types/bit_mask/main.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
OpenModeIn = 1 // 0000 0001
|
||||||
|
OpenModeOut = 2 // 0000 0010
|
||||||
|
OpenModeAppend = 4 // 0000 0100
|
||||||
|
OpenModeBinary = 8 // 0000 1000
|
||||||
|
|
||||||
|
// sugar for prepared masks
|
||||||
|
OpenModeInAndOut = OpenModeIn | OpenModeOut // 0000 0001 + 0000 0010 = 0000 0011
|
||||||
|
)
|
||||||
|
|
||||||
|
func Open(filename string, mask int8) {
|
||||||
|
if mask&OpenModeIn != 0 {
|
||||||
|
fmt.Println("in mode")
|
||||||
|
}
|
||||||
|
if mask&OpenModeOut != 0 {
|
||||||
|
fmt.Println("out mode")
|
||||||
|
}
|
||||||
|
if mask&OpenModeAppend != 0 {
|
||||||
|
fmt.Println("append mode")
|
||||||
|
}
|
||||||
|
if mask&OpenModeBinary != 0 {
|
||||||
|
fmt.Println("binary mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation...
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
Open("data.bin", OpenModeIn|OpenModeBinary) // 0000 1001
|
||||||
|
Open("data.bin", OpenModeIn|OpenModeOut) // 0000 0011
|
||||||
|
Open("data.bin", OpenModeAppend) // 0000 0100
|
||||||
|
}
|
||||||
17
lessons/data_types/bit_wrappers/main.go
Normal file
17
lessons/data_types/bit_wrappers/main.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func IsSetBit(number, index int) bool {
|
||||||
|
return (number & (1 << index)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBit(number, index int) int {
|
||||||
|
return number | (1 << index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InverseBit(number, index int) int {
|
||||||
|
return number ^ (1 << index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetBit(number, index int) int {
|
||||||
|
return number & ^(1 << index)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user