Исходники и презентации
This commit is contained in:
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)
|
||||
}
|
||||
32
lessons/data_types/bitmap_index/main.go
Normal file
32
lessons/data_types/bitmap_index/main.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
// 0000 0001 -> есть кальяны
|
||||
// 0000 0010 -> можно с животными
|
||||
// 0000 0100 -> есть виранда
|
||||
// 0000 1000 -> есть алкоголь
|
||||
// 0001 0000 -> есть живая музыка
|
||||
|
||||
func searchRestaurants(pattern int8, bitmaps []int8) []int {
|
||||
var indexes []int
|
||||
for idx, bitmap := range bitmaps {
|
||||
if bitmap^pattern == 0 {
|
||||
indexes = append(indexes, idx)
|
||||
}
|
||||
}
|
||||
|
||||
return indexes
|
||||
}
|
||||
|
||||
func main() {
|
||||
restaurants := []int8{
|
||||
0b00001101,
|
||||
0b00000010,
|
||||
0b00010000,
|
||||
0b00011111,
|
||||
0b00001001,
|
||||
}
|
||||
|
||||
pattern := int8(0b00011000)
|
||||
indexes := searchRestaurants(pattern, restaurants)
|
||||
_ = indexes
|
||||
}
|
||||
39
lessons/data_types/bool_mask/main.go
Normal file
39
lessons/data_types/bool_mask/main.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Mode struct {
|
||||
OpenModeIn bool
|
||||
OpenModeOut bool
|
||||
OpenModeAppend bool
|
||||
OpenModeBinary bool
|
||||
}
|
||||
|
||||
var (
|
||||
OpenModeInBinary = Mode{OpenModeIn: true, OpenModeBinary: true}
|
||||
OpenModeInOut = Mode{OpenModeIn: true, OpenModeOut: true}
|
||||
OpenModeAppend = Mode{OpenModeAppend: true}
|
||||
)
|
||||
|
||||
func Open(filename string, mode Mode) {
|
||||
if mode.OpenModeIn {
|
||||
fmt.Println("in mode")
|
||||
}
|
||||
if mode.OpenModeOut {
|
||||
fmt.Println("out mode")
|
||||
}
|
||||
if mode.OpenModeAppend {
|
||||
fmt.Println("append mode")
|
||||
}
|
||||
if mode.OpenModeBinary {
|
||||
fmt.Println("binary mode")
|
||||
}
|
||||
|
||||
// implementation...
|
||||
}
|
||||
|
||||
func main() {
|
||||
Open("data.bin", OpenModeInBinary)
|
||||
Open("data.bin", OpenModeInOut)
|
||||
Open("data.bin", OpenModeAppend)
|
||||
}
|
||||
31
lessons/data_types/conversion_test/conversion_test.go
Normal file
31
lessons/data_types/conversion_test/conversion_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. conversion_test.go
|
||||
|
||||
var result int
|
||||
|
||||
func BenchmarkConversion1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
result = 10 << 20
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConversion2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
result = 10 * 1024 * 1024
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConversion3(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
result = 10 * (int)(math.Pow(2, 20))
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
68
lessons/data_types/duplicate_check/check_test.go
Normal file
68
lessons/data_types/duplicate_check/check_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. check_test.go
|
||||
|
||||
func HasDuplicatesFrom1To7WithBits(data []int) bool {
|
||||
var lookup int8
|
||||
for _, number := range data {
|
||||
if lookup&(1<<number) != 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
lookup |= 1 << number
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func HasDuplicatesFrom1To7WithArray(data []int) bool {
|
||||
var lookup [8]int8
|
||||
for _, number := range data {
|
||||
if lookup[number] == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
lookup[number] = 1
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func HasDuplicatesFrom1To7WithHashTable(data []int) bool {
|
||||
lookup := make(map[int]struct{}, 8)
|
||||
for _, number := range data {
|
||||
_, found := lookup[number]
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
|
||||
lookup[number] = struct{}{}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func BenchmarkHasDuplicatesFrom1To7WithBits(b *testing.B) {
|
||||
data := []int{1, 5, 2, 3, 6, 4, 7, 2, 7}
|
||||
for i := 1; i < b.N; i++ {
|
||||
_ = HasDuplicatesFrom1To7WithBits(data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHasDuplicatesFrom1To7WithArray(b *testing.B) {
|
||||
data := []int{1, 5, 2, 3, 6, 4, 7, 2, 7}
|
||||
for i := 1; i < b.N; i++ {
|
||||
_ = HasDuplicatesFrom1To7WithArray(data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHasDuplicatesFrom1To7WithHashTable(b *testing.B) {
|
||||
data := []int{1, 5, 2, 3, 6, 4, 7, 2, 7}
|
||||
for i := 1; i < b.N; i++ {
|
||||
_ = HasDuplicatesFrom1To7WithHashTable(data)
|
||||
}
|
||||
}
|
||||
19
lessons/data_types/endianness/main.go
Normal file
19
lessons/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
lessons/data_types/endianness_check/main.go
Normal file
24
lessons/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")
|
||||
}
|
||||
}
|
||||
23
lessons/data_types/even_test/even_test.go
Normal file
23
lessons/data_types/even_test/even_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. even_test.go
|
||||
|
||||
var result int
|
||||
|
||||
func BenchmarkEven1(b *testing.B) {
|
||||
for i := 1; i < b.N; i++ {
|
||||
result = i & 1
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEven2(b *testing.B) {
|
||||
for i := 1; i < b.N; i++ {
|
||||
result = i % 2
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
15
lessons/data_types/find_unique/main.go
Normal file
15
lessons/data_types/find_unique/main.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var array = [...]int8{2, 3, 4, 2, 3, 1, 4}
|
||||
|
||||
var number int8
|
||||
for _, element := range array {
|
||||
fmt.Printf("%08b ^ %08b = %08b\n", number, element, number^element)
|
||||
number ^= element
|
||||
}
|
||||
|
||||
fmt.Printf("\nResult: %08b\n", number)
|
||||
}
|
||||
20
lessons/data_types/int_overflow/main.go
Normal file
20
lessons/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
lessons/data_types/inversion/main.go
Normal file
11
lessons/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)
|
||||
}
|
||||
38
lessons/data_types/ipv4_address/main.go
Normal file
38
lessons/data_types/ipv4_address/main.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Convert(address string) (uint32, error) {
|
||||
const octetsCount = 4
|
||||
segments := strings.Split(address, ".")
|
||||
if len(segments) != octetsCount {
|
||||
return 0, fmt.Errorf("invalid IPv4 address")
|
||||
}
|
||||
|
||||
var result uint32 // also possible [4]byte
|
||||
for idx := 0; idx < octetsCount; idx++ {
|
||||
number, err := strconv.Atoi(segments[idx])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if number < 0 || number > 255 {
|
||||
return 0, fmt.Errorf("invalid IPv4 address")
|
||||
}
|
||||
|
||||
bitOffset := (octetsCount - idx - 1) * 8
|
||||
result |= uint32(number << bitOffset)
|
||||
fmt.Printf("%08b = %d\n", number, number)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
address, _ := Convert("255.255.6.0")
|
||||
fmt.Printf("Address: %b = %d\n", address, address)
|
||||
}
|
||||
8
lessons/data_types/octal_system/main.go
Normal file
8
lessons/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
lessons/data_types/overflow_detection/main.go
Normal file
52
lessons/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
|
||||
}
|
||||
16
lessons/data_types/pointer/main.go
Normal file
16
lessons/data_types/pointer/main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var value int32 = 100
|
||||
var pointer *int32 = &value
|
||||
|
||||
fmt.Println("address:", pointer)
|
||||
fmt.Println("value:", *pointer)
|
||||
|
||||
*pointer = 500
|
||||
|
||||
fmt.Println("address:", pointer)
|
||||
fmt.Println("value:", *pointer)
|
||||
}
|
||||
21
lessons/data_types/pointer_to_pointer_correct/main.go
Normal file
21
lessons/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("value:", *pointer)
|
||||
fmt.Println("address:", pointer)
|
||||
|
||||
process(&pointer)
|
||||
|
||||
fmt.Println("value", *pointer)
|
||||
fmt.Println("address:", pointer)
|
||||
}
|
||||
21
lessons/data_types/pointer_to_pointer_incorrect/main.go
Normal file
21
lessons/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("value:", *pointer)
|
||||
fmt.Println("adress:", pointer)
|
||||
|
||||
process(pointer)
|
||||
|
||||
fmt.Println("value:", *pointer)
|
||||
fmt.Println("adress:", pointer)
|
||||
}
|
||||
17
lessons/data_types/pointer_with_function/main.go
Normal file
17
lessons/data_types/pointer_with_function/main.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Need to show solution
|
||||
|
||||
func double(number int) {
|
||||
fmt.Println("address:", &number)
|
||||
number *= 2
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 100
|
||||
double(number)
|
||||
fmt.Println("number: ", number)
|
||||
fmt.Println("address:", &number)
|
||||
}
|
||||
17
lessons/data_types/power_of_two/main.go
Normal file
17
lessons/data_types/power_of_two/main.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
// 0000 0000 = 0
|
||||
// 0000 0001 = 1
|
||||
// 0000 0010 = 2
|
||||
// 0000 0100 = 4
|
||||
// 0000 1000 = 8
|
||||
// 0001 0000 = 16
|
||||
// 0010 0000 = 32
|
||||
// 0100 0000 = 64
|
||||
// 1000 0000 = 128
|
||||
|
||||
// 0000 1111
|
||||
|
||||
func IsPowerOfTwo(value int) bool {
|
||||
return value > 0 && value&(value-1) == 0
|
||||
}
|
||||
26
lessons/data_types/uintptr_gc/main.go
Normal file
26
lessons/data_types/uintptr_gc/main.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// go run main.go
|
||||
// go run -gcflags=-d=checkptr main.go
|
||||
|
||||
func main() {
|
||||
x := new(int)
|
||||
y := new(int)
|
||||
|
||||
ptrX := unsafe.Pointer(x)
|
||||
addressY := uintptr(unsafe.Pointer(y))
|
||||
|
||||
// arithmetic operation
|
||||
_ = addressY + 2
|
||||
_ = addressY - 2
|
||||
|
||||
runtime.GC()
|
||||
|
||||
*(*int)(ptrX) = 100
|
||||
*(*int)(unsafe.Pointer(addressY)) = 300 // dangerous
|
||||
}
|
||||
27
lessons/data_types/uintptr_stack_grow/main.go
Normal file
27
lessons/data_types/uintptr_stack_grow/main.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// go run main.go
|
||||
// go run -gcflags=-d=checkptr main.go
|
||||
|
||||
//go:noinline
|
||||
func allocation(index int) byte {
|
||||
var data [1 << 20]byte
|
||||
return data[index]
|
||||
}
|
||||
|
||||
func main() {
|
||||
var array [10]int
|
||||
address1 := (uintptr)(unsafe.Pointer(&array))
|
||||
fmt.Println("address1:", address1)
|
||||
|
||||
allocation(100)
|
||||
|
||||
address2 := (uintptr)(unsafe.Pointer(&array))
|
||||
fmt.Println("address2:", address2)
|
||||
fmt.Println("address1:", address1)
|
||||
}
|
||||
20
lessons/data_types/unsafe_analysis/main.go
Normal file
20
lessons/data_types/unsafe_analysis/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
x, y int = 1, 2
|
||||
)
|
||||
|
||||
p1 := unsafe.Pointer(&x)
|
||||
p2 := unsafe.Add(p1, 8)
|
||||
|
||||
fmt.Println("p1: ", *(*int)(p1))
|
||||
fmt.Println("p2: ", *(*int)(p2))
|
||||
|
||||
_ = y
|
||||
}
|
||||
20
lessons/data_types/unsafe_pointer/main.go
Normal file
20
lessons/data_types/unsafe_pointer/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var value uint32 = 0xFFFFFFFF
|
||||
|
||||
pointer := unsafe.Pointer(&value)
|
||||
bytePointer := (*uint8)(pointer)
|
||||
|
||||
fmt.Println("value1:", *bytePointer)
|
||||
|
||||
pointer = unsafe.Add(pointer, 2)
|
||||
twoBytePointer := (*uint16)(pointer)
|
||||
|
||||
fmt.Println("value2:", *twoBytePointer)
|
||||
}
|
||||
14
lessons/data_types/unsafe_size_of/main.go
Normal file
14
lessons/data_types/unsafe_size_of/main.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var value1 int8 = 10
|
||||
fmt.Println("size1:", unsafe.Sizeof(value1)) // compile time
|
||||
|
||||
var value2 int32 = 10
|
||||
fmt.Println("size2:", unsafe.Sizeof(value2)) // compile time
|
||||
}
|
||||
12
lessons/environment/init_in_any_files/main.go
Normal file
12
lessons/environment/init_in_any_files/main.go
Normal file
@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func init() {
|
||||
fmt.Println("init[main.go] called")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("main called")
|
||||
secondary()
|
||||
}
|
||||
11
lessons/environment/init_in_any_files/secondary.go
Normal file
11
lessons/environment/init_in_any_files/secondary.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func init() {
|
||||
fmt.Println("init[secondary.go] called")
|
||||
}
|
||||
|
||||
func secondary() {
|
||||
fmt.Println("secondary called")
|
||||
}
|
||||
15
lessons/environment/init_in_any_packages/main/main.go
Normal file
15
lessons/environment/init_in_any_packages/main/main.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang_course/environment/init_in_any_packages/secondary"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("init[main.go] called")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("main called")
|
||||
secondary.Secondary()
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package secondary
|
||||
|
||||
import "fmt"
|
||||
|
||||
func init() {
|
||||
fmt.Println("init[secondary.go] called")
|
||||
}
|
||||
|
||||
func Secondary() {
|
||||
fmt.Println("secondary called")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user