mirror of
https://gitee.com/johng/gf.git
synced 2024-12-01 03:38:35 +08:00
Merge branch 'master' into 完善garray测试
This commit is contained in:
commit
3bfff2347f
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,7 +7,6 @@
|
||||
.settings/
|
||||
.vscode/
|
||||
vender/
|
||||
log/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
|
||||
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
|
||||
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
|
||||
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
|
||||
|
@ -9,11 +9,12 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type IntArray struct {
|
||||
@ -266,31 +267,83 @@ func (a *IntArray) PopRights(size int) []int {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *IntArray) Range(start, end int) []int {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *IntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]int, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *IntArray) SubSlice(offset int, length ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
func (a *IntArray) Append(value ...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
@ -488,27 +541,6 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *IntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *IntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
|
@ -9,11 +9,12 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Array struct {
|
||||
@ -262,31 +263,83 @@ func (a *Array) PopRights(size int) []interface{} {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *Array) Range(start, end int) []interface{} {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *Array) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]interface{}, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *Array) SubSlice(offset int, length ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
func (a *Array) Append(value ...interface{}) *Array {
|
||||
a.PushRight(value...)
|
||||
@ -482,27 +535,6 @@ func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *Array) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *Array) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
|
@ -9,12 +9,13 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
)
|
||||
|
||||
type StringArray struct {
|
||||
@ -267,31 +268,83 @@ func (a *StringArray) PopRights(size int) []string {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *StringArray) Range(start, end int) []string {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *StringArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]string, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *StringArray) SubSlice(offset int, length ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
func (a *StringArray) Append(value ...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
@ -488,27 +541,6 @@ func (a *StringArray) Pad(size int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *StringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *StringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
|
@ -9,12 +9,13 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
@ -216,31 +217,83 @@ func (a *SortedIntArray) PopRights(size int) []int {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *SortedIntArray) Range(start, end int) []int {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedIntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]int, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedIntArray) SubSlice(offset int, length ...int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the length of array.
|
||||
func (a *SortedIntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
@ -433,27 +486,6 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
return n
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedIntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedIntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
|
@ -9,12 +9,13 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
@ -217,31 +218,83 @@ func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *SortedArray) Range(start, end int) []interface{} {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedArray) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]interface{}, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedArray) SubSlice(offset int, length ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
@ -434,27 +487,6 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedArray) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedArray) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
|
@ -9,13 +9,14 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
@ -211,31 +212,83 @@ func (a *SortedStringArray) PopRights(size int) []string {
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
func (a *SortedStringArray) Range(start, end int) []string {
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedStringArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
offsetEnd := len(a.array)
|
||||
if len(end) > 0 && end[0] < offsetEnd {
|
||||
offsetEnd = end[0]
|
||||
}
|
||||
if start > offsetEnd {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, end-start)
|
||||
copy(array, a.array[start:end])
|
||||
array = make([]string, offsetEnd-start)
|
||||
copy(array, a.array[start:offsetEnd])
|
||||
} else {
|
||||
array = a.array[start:end]
|
||||
array = a.array[start:offsetEnd]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
// If offset is negative, the sequence will start that far from the end of the array.
|
||||
//
|
||||
// If length is given and is positive, then the sequence will have up to that many elements in it.
|
||||
// If the array is shorter than the length, then only the available array elements will be present.
|
||||
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
|
||||
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
|
||||
//
|
||||
// Any possibility crossing the left border of array, it will fail.
|
||||
func (a *SortedStringArray) SubSlice(offset int, length ...int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
size := len(a.array)
|
||||
if len(length) > 0 {
|
||||
size = length[0]
|
||||
}
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = len(a.array) + offset
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if size < 0 {
|
||||
offset += size
|
||||
size = -size
|
||||
if offset < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
end := offset + size
|
||||
if end > len(a.array) {
|
||||
end = len(a.array)
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:end]
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedStringArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
@ -428,27 +481,6 @@ func (a *SortedStringArray) Chunk(size int) [][]string {
|
||||
return n
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedStringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset+size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedStringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
|
@ -11,10 +11,7 @@ package garray_test
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_IntArray_Basic(t *testing.T) {
|
||||
@ -49,7 +46,7 @@ func TestIntArray_Sort(t *testing.T) {
|
||||
expect1 := []int{0, 1, 2, 3}
|
||||
expect2 := []int{3, 2, 1, 0}
|
||||
array := garray.NewIntArray()
|
||||
array2:=garray.NewIntArray(true)
|
||||
array2 := garray.NewIntArray(true)
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
array2.Append(i)
|
||||
@ -62,7 +59,6 @@ func TestIntArray_Sort(t *testing.T) {
|
||||
array2.Sort(true)
|
||||
gtest.Assert(array2.Slice(), expect2)
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -110,10 +106,11 @@ func TestIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value1,true)
|
||||
array2 := garray.NewIntArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []int{0})
|
||||
gtest.Assert(array1.Range(1, 2), []int{1})
|
||||
gtest.Assert(array1.Range(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array1.Range(8, 2), nil)
|
||||
|
||||
@ -124,32 +121,32 @@ func TestIntArray_Range(t *testing.T) {
|
||||
func TestIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
n1 := []int{1, 2, 4, 3}
|
||||
n2:=[]int{7,8,9}
|
||||
n3:=[]int{3,6}
|
||||
n2 := []int{7, 8, 9}
|
||||
n3 := []int{3, 6}
|
||||
|
||||
s1:=[]string{"a","b","c"}
|
||||
in1:=[]interface{}{1,"a",2,"b"}
|
||||
s1 := []string{"a", "b", "c"}
|
||||
in1 := []interface{}{1, "a", 2, "b"}
|
||||
|
||||
func1:=func(v1,v2 interface{})int{
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
|
||||
a1 := garray.NewIntArrayFrom(n1)
|
||||
b1 := garray.NewStringArrayFrom(s1)
|
||||
b2:=garray.NewIntArrayFrom(n3)
|
||||
b3:=garray.NewArrayFrom(in1)
|
||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
||||
b6:=garray.NewSortedArrayFrom(in1,func1)
|
||||
b2 := garray.NewIntArrayFrom(n3)
|
||||
b3 := garray.NewArrayFrom(in1)
|
||||
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||
b6 := garray.NewSortedArrayFrom(in1, func1)
|
||||
|
||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
||||
gtest.Assert(a1.Merge(b6).Len(),27)
|
||||
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||
gtest.Assert(a1.Merge(b6).Len(), 27)
|
||||
})
|
||||
}
|
||||
|
||||
@ -192,10 +189,21 @@ func TestIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.SubSlice(6), []int{6})
|
||||
gtest.Assert(array1.SubSlice(5), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(8), nil)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(8, 2), nil)
|
||||
gtest.Assert(array1.SubSlice(-1, 1), []int{6})
|
||||
gtest.Assert(array1.SubSlice(-1, 9), []int{6})
|
||||
gtest.Assert(array1.SubSlice(-2, 3), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2})
|
||||
gtest.Assert(array1.SubSlice(-8, 3), nil)
|
||||
gtest.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5})
|
||||
gtest.Assert(array1.SubSlice(-9, 3), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -1), []int{0})
|
||||
gtest.Assert(array1.SubSlice(1, -3), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -417,7 +425,7 @@ func TestSortedIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{1, 3, 5, 2, 6, 7}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1,true)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
ns1 := array1.Range(1, 4)
|
||||
gtest.Assert(len(ns1), 3)
|
||||
gtest.Assert(ns1, []int{2, 3, 5})
|
||||
@ -482,7 +490,6 @@ func TestSortedIntArray_Chunk(t *testing.T) {
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
|
||||
ns2 := array1.Chunk(-1)
|
||||
t.Log(ns1)
|
||||
gtest.Assert(len(ns1), 3)
|
||||
gtest.Assert(ns1[0], []int{1, 2})
|
||||
gtest.Assert(ns1[2], []int{5})
|
||||
@ -711,37 +718,34 @@ func TestSortedIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.Assert(a1.Contains(7), true)
|
||||
}
|
||||
|
||||
|
||||
func TestSortedIntArray_Merge(t *testing.T) {
|
||||
n1 := []int{1, 2, 4, 3}
|
||||
n2:=[]int{7,8,9}
|
||||
n3:=[]int{3,6}
|
||||
|
||||
s1:=[]string{"a","b","c"}
|
||||
in1:=[]interface{}{1,"a",2,"b"}
|
||||
n2 := []int{7, 8, 9}
|
||||
n3 := []int{3, 6}
|
||||
|
||||
s1 := []string{"a", "b", "c"}
|
||||
in1 := []interface{}{1, "a", 2, "b"}
|
||||
|
||||
a1 := garray.NewSortedIntArrayFrom(n1)
|
||||
b1 := garray.NewStringArrayFrom(s1)
|
||||
b2:=garray.NewIntArrayFrom(n3)
|
||||
b3:=garray.NewArrayFrom(in1)
|
||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
||||
b2 := garray.NewIntArrayFrom(n3)
|
||||
b3 := garray.NewArrayFrom(in1)
|
||||
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||
|
||||
|
||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
||||
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||
}
|
||||
|
||||
func TestSortedArray_LockFunc(t *testing.T) {
|
||||
n1 := []interface{}{1, 2, 4, 3}
|
||||
|
||||
func1:=func(v1,v2 interface{})int{
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||
@ -769,7 +773,7 @@ func TestSortedArray_LockFunc(t *testing.T) {
|
||||
func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
n1 := []interface{}{1, 2, 4, 3}
|
||||
|
||||
func1:=func(v1,v2 interface{})int{
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||
@ -795,56 +799,54 @@ func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
gtest.Assert(a1.Contains(7), true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func TestSortedArray_Merge(t *testing.T) {
|
||||
n1 := []interface{}{1, 2, 4, 3}
|
||||
n2:=[]int{7,8,9}
|
||||
n3:=[]int{3,6}
|
||||
n2 := []int{7, 8, 9}
|
||||
n3 := []int{3, 6}
|
||||
|
||||
s1:=[]string{"a","b","c"}
|
||||
in1:=[]interface{}{1,"a",2,"b"}
|
||||
s1 := []string{"a", "b", "c"}
|
||||
in1 := []interface{}{1, "a", 2, "b"}
|
||||
|
||||
func1:=func(v1,v2 interface{})int{
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
|
||||
a1 := garray.NewSortedArrayFrom(n1,func1)
|
||||
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||
b1 := garray.NewStringArrayFrom(s1)
|
||||
b2:=garray.NewIntArrayFrom(n3)
|
||||
b3:=garray.NewArrayFrom(in1)
|
||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
||||
b2 := garray.NewIntArrayFrom(n3)
|
||||
b3 := garray.NewArrayFrom(in1)
|
||||
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||
|
||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
||||
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||
}
|
||||
|
||||
func TestIntArray_SortFunc(t *testing.T) {
|
||||
n1:=[]int{1,2,3,5,4}
|
||||
a1:=garray.NewIntArrayFrom(n1)
|
||||
n1 := []int{1, 2, 3, 5, 4}
|
||||
a1 := garray.NewIntArrayFrom(n1)
|
||||
|
||||
func1:=func(v1,v2 int)bool{
|
||||
if v1>v2{
|
||||
func1 := func(v1, v2 int) bool {
|
||||
if v1 > v2 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func2:=func(v1,v2 int)bool{
|
||||
if v1>v2{
|
||||
func2 := func(v1, v2 int) bool {
|
||||
if v1 > v2 {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
a2:=a1.SortFunc(func1)
|
||||
gtest.Assert(a2,[]int{1,2,3,4,5})
|
||||
a3:=a1.SortFunc(func2)
|
||||
gtest.Assert(a3,[]int{5,4,3,2,1})
|
||||
a2 := a1.SortFunc(func1)
|
||||
gtest.Assert(a2, []int{1, 2, 3, 4, 5})
|
||||
a3 := a1.SortFunc(func2)
|
||||
gtest.Assert(a3, []int{5, 4, 3, 2, 1})
|
||||
|
||||
}
|
||||
|
||||
@ -895,4 +897,3 @@ func TestIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.AssertLT(t2-t1, 20)
|
||||
gtest.Assert(a1.Contains(7), true)
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,12 @@ package gpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/os/gtimer"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Object-Reusable Pool.
|
||||
@ -126,6 +127,7 @@ func (p *Pool) checkExpire() {
|
||||
gtimer.Exit()
|
||||
}
|
||||
for {
|
||||
// TODO Do not use Pop and Push mechanism, which is not graceful.
|
||||
if r := p.list.PopFront(); r != nil {
|
||||
item := r.(*poolItem)
|
||||
if item.expire == 0 || item.expire > gtime.Millisecond() {
|
||||
|
@ -41,7 +41,7 @@ func (v *Int64) Val() int64 {
|
||||
}
|
||||
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (v *Int64) Add(delta int64) int64 {
|
||||
func (v *Int64) Add(delta int64) (new int64) {
|
||||
return atomic.AddInt64(&v.value, delta)
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/g/crypto/gmd5"
|
||||
|
||||
"github.com/gogf/gf/g/crypto/gcrc32"
|
||||
"github.com/gogf/gf/g/crypto/gmd5"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// Encrypt encrypts any type of variable using MD5 algorithms.
|
||||
@ -40,9 +40,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if e := f.Close(); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
@ -10,11 +10,10 @@ package gsha1
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// Encrypt encrypts any type of variable using SHA1 algorithms.
|
||||
@ -37,9 +36,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if e := f.Close(); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := sha1.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
@ -7,9 +7,10 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Instance(t *testing.T) {
|
||||
|
@ -8,12 +8,13 @@
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -26,8 +27,14 @@ type Json struct {
|
||||
mu *rwmutex.RWMutex
|
||||
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
|
||||
c byte // Char separator('.' in default).
|
||||
vc bool // Violence Check(false in default), which is used to access data
|
||||
// Violence Check(false in default), which is used to access data
|
||||
// when the hierarchical data key contains separator char.
|
||||
vc bool
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (j *Json) MarshalJSON() ([]byte, error) {
|
||||
return j.ToJson()
|
||||
}
|
||||
|
||||
// Set <value> by <pattern>.
|
||||
|
@ -6,6 +6,11 @@
|
||||
|
||||
package gparser
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (p *Parser) MarshalJSON() ([]byte, error) {
|
||||
return p.json.MarshalJSON()
|
||||
}
|
||||
|
||||
func (p *Parser) ToXml(rootTag ...string) ([]byte, error) {
|
||||
return p.json.ToXml(rootTag...)
|
||||
}
|
||||
|
@ -8,11 +8,12 @@ package gins_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/frame/gins"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_View(t *testing.T) {
|
||||
|
2
g/g.go
2
g/g.go
@ -39,6 +39,8 @@ type Slice = []interface{}
|
||||
type SliceAny = []interface{}
|
||||
type SliceStr = []string
|
||||
type SliceInt = []int
|
||||
|
||||
// Array is alias of Slice.
|
||||
type Array = []interface{}
|
||||
type ArrayAny = []interface{}
|
||||
type ArrayStr = []string
|
||||
|
@ -39,7 +39,7 @@ func Throw(exception interface{}) {
|
||||
gutil.Throw(exception)
|
||||
}
|
||||
|
||||
// TryCatch does the try...catch... logic.
|
||||
// TryCatch does the try...catch... mechanism.
|
||||
func TryCatch(try func(), catch ...func(exception interface{})) {
|
||||
gutil.TryCatch(try, catch...)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
// SetDebug disables/enables debug level for logging globally.
|
||||
// SetDebug disables/enables debug level for logging component globally.
|
||||
func SetDebug(debug bool) {
|
||||
glog.SetDebug(debug)
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ func Database(name ...string) gdb.DB {
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
||||
// DB is alias of Database. See Database.
|
||||
// DB is alias of Database.
|
||||
// See Database.
|
||||
func DB(name ...string) gdb.DB {
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package g
|
||||
|
||||
import "github.com/gogf/gf/g/net/ghttp"
|
||||
|
||||
// SetServerGraceful enables/disables graceful/hot reload feature of http Web Server.
|
||||
// SetServerGraceful enables/disables graceful reload feature of http Web Server.
|
||||
// This feature is disabled in default.
|
||||
func SetServerGraceful(enabled bool) {
|
||||
ghttp.SetGraceful(enabled)
|
||||
|
@ -9,9 +9,10 @@
|
||||
package cmdenv
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Get(t *testing.T) {
|
||||
|
48
g/internal/errors/errors.go
Normal file
48
g/internal/errors/errors.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package errors provides simple functions to manipulate errors.
|
||||
//
|
||||
// This package can be scalable due to https://go.googlesource.com/proposal/+/master/design/go2draft.md.
|
||||
package errors
|
||||
|
||||
import "github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
// errorWrapper is a simple wrapper for errors.
|
||||
type errorWrapper struct {
|
||||
s string
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given value.
|
||||
func New(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(gconv.String(value))
|
||||
}
|
||||
|
||||
// NewText returns an error that formats as the given text.
|
||||
func NewText(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &errorWrapper{
|
||||
s: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(text + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Error implements interface Error.
|
||||
func (e *errorWrapper) Error() string {
|
||||
return e.s
|
||||
}
|
39
g/internal/errors/errors_test.go
Normal file
39
g/internal/errors/errors_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(errors.New(interfaceNil()), nil)
|
||||
gtest.Assert(errors.Wrap(nilError(), "test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Wrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = errors.Wrap(err, "func2 error")
|
||||
err = errors.Wrap(err, "func3 error")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "func3 error: func2 error: 1")
|
||||
})
|
||||
}
|
@ -8,7 +8,6 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -26,7 +25,7 @@ type CORSOptions struct {
|
||||
// 默认的CORS配置
|
||||
func (r *Response) DefaultCORSOptions() CORSOptions {
|
||||
return CORSOptions{
|
||||
AllowOrigin: gstr.TrimRight(r.request.Referer(), "/"),
|
||||
AllowOrigin: "*",
|
||||
AllowMethods: HTTP_METHODS,
|
||||
AllowCredentials: "true",
|
||||
MaxAge: 3628800,
|
||||
|
@ -10,6 +10,14 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
@ -23,13 +31,6 @@ import (
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/third/github.com/gorilla/websocket"
|
||||
"github.com/gogf/gf/third/github.com/olekukonko/tablewriter"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -382,15 +383,14 @@ func (s *Server) GetRouteMap() string {
|
||||
}
|
||||
|
||||
// 阻塞执行监听
|
||||
func (s *Server) Run() error {
|
||||
func (s *Server) Run() {
|
||||
if err := s.Start(); err != nil {
|
||||
return err
|
||||
glog.Fatal(err)
|
||||
}
|
||||
// 阻塞等待服务执行完成
|
||||
<-s.closeChan
|
||||
|
||||
glog.Printf("%d: all servers shutdown", gproc.Pid())
|
||||
return nil
|
||||
}
|
||||
|
||||
// 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
)
|
||||
|
||||
// 封装的链接对象
|
||||
@ -208,9 +210,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -222,9 +222,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
@ -8,9 +8,10 @@ package gtcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -73,9 +74,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
@ -148,9 +147,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
|
@ -7,11 +7,10 @@
|
||||
package gtcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gpool"
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 链接池链接对象
|
||||
@ -121,9 +120,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -135,9 +132,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
@ -9,6 +9,8 @@ package gtcp
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
)
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据
|
||||
@ -46,9 +48,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.RecvPkg(option...)
|
||||
return
|
||||
@ -60,9 +60,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.SendPkg(data, option...)
|
||||
return
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
)
|
||||
|
||||
// 封装的UDP链接对象
|
||||
@ -181,9 +183,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||
}()
|
||||
data, err = c.Recv(length, retry...)
|
||||
return
|
||||
@ -195,9 +195,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
||||
err = errors.New(err.Error() + "; " + e.Error())
|
||||
}
|
||||
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||
}()
|
||||
err = c.Send(data, retry...)
|
||||
return
|
||||
|
@ -7,11 +7,12 @@
|
||||
package gcron_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/os/gcron"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCron_Add_Close(t *testing.T) {
|
||||
@ -146,7 +147,7 @@ func TestCron_AddOnce2(t *testing.T) {
|
||||
array.Append(1)
|
||||
})
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
time.Sleep(2500 * time.Millisecond)
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
gtest.Assert(cron.Size(), 0)
|
||||
})
|
||||
|
@ -10,13 +10,11 @@ package gflock
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// File locker.
|
||||
type Locker struct {
|
||||
mu sync.RWMutex // 用于外部接口调用的互斥锁(阻塞机制)
|
||||
flock *flock.Flock // 底层文件锁对象
|
||||
flock *flock.Flock // Underlying file locker.
|
||||
}
|
||||
|
||||
// New creates and returns a new file locker with given <file>.
|
||||
@ -47,9 +45,6 @@ func (l *Locker) IsLocked() bool {
|
||||
// It returns true if success, or else returns false immediately.
|
||||
func (l *Locker) TryLock() bool {
|
||||
ok, _ := l.flock.TryLock()
|
||||
if ok {
|
||||
l.mu.Lock()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -57,32 +52,61 @@ func (l *Locker) TryLock() bool {
|
||||
// It returns true if success, or else returns false immediately.
|
||||
func (l *Locker) TryRLock() bool {
|
||||
ok, _ := l.flock.TryRLock()
|
||||
if ok {
|
||||
l.mu.RLock()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Lock is a blocking call to try and take an exclusive file lock. It will wait
|
||||
// until it is able to obtain the exclusive file lock. It's recommended that
|
||||
// TryLock() be used over this function. This function may block the ability to
|
||||
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||
//
|
||||
// If we are already exclusive-locked, this function short-circuits and returns
|
||||
// immediately assuming it can take the mutex lock.
|
||||
//
|
||||
// If the *Flock has a shared lock (RLock), this may transparently replace the
|
||||
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
|
||||
// careful when using exclusive locks in conjunction with shared locks
|
||||
// (RLock()), because calling Unlock() may accidentally release the exclusive
|
||||
// lock that was once a shared lock.
|
||||
func (l *Locker) Lock() (err error) {
|
||||
l.mu.Lock()
|
||||
err = l.flock.Lock()
|
||||
return
|
||||
return l.flock.Lock()
|
||||
}
|
||||
|
||||
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||
//
|
||||
// This function short-circuits if we are unlocked already. If not, it calls
|
||||
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
|
||||
// remove the file from disk. It's up to your application to do.
|
||||
//
|
||||
// Please note, if your shared lock became an exclusive lock this may
|
||||
// unintentionally drop the exclusive lock if called by the consumer that
|
||||
// believes they have a shared lock. Please see Lock() for more details.
|
||||
func (l *Locker) UnLock() (err error) {
|
||||
err = l.flock.Unlock()
|
||||
l.mu.Unlock()
|
||||
return
|
||||
return l.flock.Unlock()
|
||||
}
|
||||
|
||||
// RLock is a blocking call to try and take a ahred file lock. It will wait
|
||||
// until it is able to obtain the shared file lock. It's recommended that
|
||||
// TryRLock() be used over this function. This function may block the ability to
|
||||
// query the current Locked() or RLocked() status due to a RW-mutex lock.
|
||||
//
|
||||
// If we are already shared-locked, this function short-circuits and returns
|
||||
// immediately assuming it can take the mutex lock.
|
||||
func (l *Locker) RLock() (err error) {
|
||||
l.mu.RLock()
|
||||
err = l.flock.RLock()
|
||||
return
|
||||
return l.flock.RLock()
|
||||
}
|
||||
|
||||
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
|
||||
// while it is running the Locked() and RLocked() functions will be blocked.
|
||||
//
|
||||
// This function short-circuits if we are unlocked already. If not, it calls
|
||||
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
|
||||
// remove the file from disk. It's up to your application to do.
|
||||
//
|
||||
// Please note, if your shared lock became an exclusive lock this may
|
||||
// unintentionally drop the exclusive lock if called by the consumer that
|
||||
// believes they have a shared lock. Please see Lock() for more details.
|
||||
func (l *Locker) RUnlock() (err error) {
|
||||
err = l.flock.Unlock()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
return l.flock.Unlock()
|
||||
}
|
||||
|
@ -7,26 +7,23 @@
|
||||
// Package gmlock implements a concurrent-safe memory-based locker.
|
||||
package gmlock
|
||||
|
||||
import "time"
|
||||
|
||||
var (
|
||||
// Default locker.
|
||||
locker = New()
|
||||
)
|
||||
|
||||
// TryLock tries locking the <key> with writing lock,
|
||||
// it returns true if success, or if there's a write/reading lock the <key>,
|
||||
// it returns false. The parameter <expire> specifies the max duration it locks.
|
||||
func TryLock(key string, expire ...time.Duration) bool {
|
||||
return locker.TryLock(key, expire...)
|
||||
}
|
||||
|
||||
// Lock locks the <key> with writing lock.
|
||||
// If there's a write/reading lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func Lock(key string, expire ...time.Duration) {
|
||||
locker.Lock(key, expire...)
|
||||
func Lock(key string) {
|
||||
locker.Lock(key)
|
||||
}
|
||||
|
||||
// TryLock tries locking the <key> with writing lock,
|
||||
// it returns true if success, or if there's a write/reading lock the <key>,
|
||||
// it returns false.
|
||||
func TryLock(key string) bool {
|
||||
return locker.TryLock(key)
|
||||
}
|
||||
|
||||
// Unlock unlocks the writing lock of the <key>.
|
||||
@ -34,12 +31,6 @@ func Unlock(key string) {
|
||||
locker.Unlock(key)
|
||||
}
|
||||
|
||||
// TryRLock tries locking the <key> with reading lock.
|
||||
// It returns true if success, or if there's a writing lock on <key>, it returns false.
|
||||
func TryRLock(key string) bool {
|
||||
return locker.TryRLock(key)
|
||||
}
|
||||
|
||||
// RLock locks the <key> with reading lock.
|
||||
// If there's a writing lock on <key>,
|
||||
// it will blocks until the writing lock is released.
|
||||
@ -47,40 +38,24 @@ func RLock(key string) {
|
||||
locker.RLock(key)
|
||||
}
|
||||
|
||||
// TryRLock tries locking the <key> with reading lock.
|
||||
// It returns true if success, or if there's a writing lock on <key>, it returns false.
|
||||
func TryRLock(key string) bool {
|
||||
return locker.TryRLock(key)
|
||||
}
|
||||
|
||||
// RUnlock unlocks the reading lock of the <key>.
|
||||
func RUnlock(key string) {
|
||||
locker.RUnlock(key)
|
||||
}
|
||||
|
||||
// TryLockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write/reading lock the <key>, it return false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func TryLockFunc(key string, f func(), expire ...time.Duration) bool {
|
||||
return locker.TryLockFunc(key, f, expire...)
|
||||
}
|
||||
|
||||
// TryRLockFunc locks the <key> with reading lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a writing lock the <key>, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func TryRLockFunc(key string, f func()) bool {
|
||||
return locker.TryRLockFunc(key, f)
|
||||
}
|
||||
|
||||
// LockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// If there's a write/reading lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func LockFunc(key string, f func(), expire ...time.Duration) {
|
||||
locker.LockFunc(key, f, expire...)
|
||||
func LockFunc(key string, f func()) {
|
||||
locker.LockFunc(key, f)
|
||||
}
|
||||
|
||||
// RLockFunc locks the <key> with reading lock and callback function <f>.
|
||||
@ -88,8 +63,27 @@ func LockFunc(key string, f func(), expire ...time.Duration) {
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func RLockFunc(key string, f func()) {
|
||||
locker.RLockFunc(key, f)
|
||||
}
|
||||
|
||||
// TryLockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write/reading lock the <key>, it return false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func TryLockFunc(key string, f func()) bool {
|
||||
return locker.TryLockFunc(key, f)
|
||||
}
|
||||
|
||||
// TryRLockFunc locks the <key> with reading lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a writing lock the <key>, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func TryRLockFunc(key string, f func()) bool {
|
||||
return locker.TryRLockFunc(key, f)
|
||||
}
|
||||
|
||||
// Remove removes mutex with given <key>.
|
||||
func Remove(key string) {
|
||||
locker.Remove(key)
|
||||
}
|
||||
|
@ -8,11 +8,12 @@ package gmlock
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/os/gtimer"
|
||||
"time"
|
||||
"github.com/gogf/gf/g/os/gmutex"
|
||||
)
|
||||
|
||||
// Memory locker.
|
||||
// Note that there's no cache expire mechanism for mutex in locker.
|
||||
// You need remove certain mutex manually when you do not want use it any more.
|
||||
type Locker struct {
|
||||
m *gmap.StrAnyMap
|
||||
}
|
||||
@ -25,56 +26,74 @@ func New() *Locker {
|
||||
}
|
||||
}
|
||||
|
||||
// TryLock tries locking the <key> with writing lock,
|
||||
// it returns true if success, or it returns false if there's a writing/reading lock the <key>.
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryLock(key string, expire ...time.Duration) bool {
|
||||
return l.doLock(key, l.getExpire(expire...), true)
|
||||
}
|
||||
|
||||
// Lock locks the <key> with writing lock.
|
||||
// If there's a write/reading lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) Lock(key string, expire ...time.Duration) {
|
||||
l.doLock(key, l.getExpire(expire...), false)
|
||||
func (l *Locker) Lock(key string) {
|
||||
l.getOrNewMutex(key).Lock()
|
||||
}
|
||||
|
||||
// TryLock tries locking the <key> with writing lock,
|
||||
// it returns true if success, or it returns false if there's a writing/reading lock the <key>.
|
||||
func (l *Locker) TryLock(key string) bool {
|
||||
return l.getOrNewMutex(key).TryLock()
|
||||
}
|
||||
|
||||
// Unlock unlocks the writing lock of the <key>.
|
||||
func (l *Locker) Unlock(key string) {
|
||||
if v := l.m.Get(key); v != nil {
|
||||
v.(*Mutex).Unlock()
|
||||
v.(*gmutex.Mutex).Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// TryRLock tries locking the <key> with reading lock.
|
||||
// It returns true if success, or if there's a writing lock on <key>, it returns false.
|
||||
func (l *Locker) TryRLock(key string) bool {
|
||||
return l.doRLock(key, true)
|
||||
}
|
||||
|
||||
// RLock locks the <key> with reading lock.
|
||||
// If there's a writing lock on <key>,
|
||||
// it will blocks until the writing lock is released.
|
||||
func (l *Locker) RLock(key string) {
|
||||
l.doRLock(key, false)
|
||||
l.getOrNewMutex(key).RLock()
|
||||
}
|
||||
|
||||
// TryRLock tries locking the <key> with reading lock.
|
||||
// It returns true if success, or if there's a writing lock on <key>, it returns false.
|
||||
func (l *Locker) TryRLock(key string) bool {
|
||||
return l.getOrNewMutex(key).TryRLock()
|
||||
}
|
||||
|
||||
// RUnlock unlocks the reading lock of the <key>.
|
||||
func (l *Locker) RUnlock(key string) {
|
||||
if v := l.m.Get(key); v != nil {
|
||||
v.(*Mutex).RUnlock()
|
||||
v.(*gmutex.Mutex).RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// LockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// If there's a write/reading lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (l *Locker) LockFunc(key string, f func()) {
|
||||
l.Lock(key)
|
||||
defer l.Unlock(key)
|
||||
f()
|
||||
}
|
||||
|
||||
// RLockFunc locks the <key> with reading lock and callback function <f>.
|
||||
// If there's a writing lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (l *Locker) RLockFunc(key string, f func()) {
|
||||
l.RLock(key)
|
||||
defer l.RUnlock(key)
|
||||
f()
|
||||
}
|
||||
|
||||
// TryLockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write/reading lock the <key>, it return false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool {
|
||||
if l.TryLock(key, expire...) {
|
||||
func (l *Locker) TryLockFunc(key string, f func()) bool {
|
||||
if l.TryLock(key) {
|
||||
defer l.Unlock(key)
|
||||
f()
|
||||
return true
|
||||
@ -86,8 +105,6 @@ func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool
|
||||
// It returns true if success, or else if there's a writing lock the <key>, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryRLockFunc(key string, f func()) bool {
|
||||
if l.TryRLock(key) {
|
||||
defer l.RUnlock(key)
|
||||
@ -97,90 +114,20 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// LockFunc locks the <key> with writing lock and callback function <f>.
|
||||
// If there's a write/reading lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) LockFunc(key string, f func(), expire ...time.Duration) {
|
||||
l.Lock(key, expire...)
|
||||
defer l.Unlock(key)
|
||||
f()
|
||||
// Remove removes mutex with given <key> from locker.
|
||||
func (l *Locker) Remove(key string) {
|
||||
l.m.Remove(key)
|
||||
}
|
||||
|
||||
// RLockFunc locks the <key> with reading lock and callback function <f>.
|
||||
// If there's a writing lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) RLockFunc(key string, f func()) {
|
||||
l.RLock(key)
|
||||
defer l.RUnlock(key)
|
||||
f()
|
||||
}
|
||||
|
||||
// getExpire returns the duration object passed.
|
||||
// If <expire> is not passed, it returns a default duration object.
|
||||
func (l *Locker) getExpire(expire ...time.Duration) time.Duration {
|
||||
e := time.Duration(0)
|
||||
if len(expire) > 0 {
|
||||
e = expire[0]
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// doLock locks writing on <key>.
|
||||
// It returns true if success, or else returns false.
|
||||
//
|
||||
// The parameter <try> is true,
|
||||
// it returns false immediately if it fails getting the writing lock.
|
||||
// If <true> is false, it blocks until it gets the writing lock.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) doLock(key string, expire time.Duration, try bool) bool {
|
||||
mu := l.getOrNewMutex(key)
|
||||
ok := true
|
||||
if try {
|
||||
ok = mu.TryLock()
|
||||
} else {
|
||||
mu.Lock()
|
||||
}
|
||||
if ok && expire > 0 {
|
||||
wid := mu.wid.Val()
|
||||
gtimer.AddOnce(expire, func() {
|
||||
if wid == mu.wid.Val() {
|
||||
mu.Unlock()
|
||||
}
|
||||
})
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// doRLock locks reading on <key>.
|
||||
// It returns true if success, or else returns false.
|
||||
//
|
||||
// The parameter <try> is true,
|
||||
// it returns false immediately if it fails getting the reading lock.
|
||||
// If <true> is false, it blocks until it gets the reading lock.
|
||||
func (l *Locker) doRLock(key string, try bool) bool {
|
||||
mu := l.getOrNewMutex(key)
|
||||
ok := true
|
||||
if try {
|
||||
ok = mu.TryRLock()
|
||||
} else {
|
||||
mu.RLock()
|
||||
}
|
||||
return ok
|
||||
// Clear removes all mutexes from locker.
|
||||
func (l *Locker) Clear() {
|
||||
l.m.Clear()
|
||||
}
|
||||
|
||||
// getOrNewMutex returns the mutex of given <key> if it exists,
|
||||
// or else creates and returns a new one.
|
||||
func (l *Locker) getOrNewMutex(key string) *Mutex {
|
||||
func (l *Locker) getOrNewMutex(key string) *gmutex.Mutex {
|
||||
return l.m.GetOrSetFuncLock(key, func() interface{} {
|
||||
return NewMutex()
|
||||
}).(*Mutex)
|
||||
return gmutex.New()
|
||||
}).(*gmutex.Mutex)
|
||||
}
|
||||
|
@ -44,58 +44,61 @@ func Test_Locker_Lock(t *testing.T) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
})
|
||||
//expire
|
||||
}
|
||||
|
||||
func Test_Locker_LockFunc(t *testing.T) {
|
||||
//no expire
|
||||
gtest.Case(t, func() {
|
||||
key := "testLockExpire"
|
||||
key := "testLockFunc"
|
||||
array := garray.New()
|
||||
go func() {
|
||||
gmlock.Lock(key, 100*time.Millisecond)
|
||||
array.Append(1)
|
||||
gmlock.LockFunc(key, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}) //
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gmlock.Lock(key)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
array.Append(1)
|
||||
gmlock.Unlock(key)
|
||||
gmlock.LockFunc(key, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1) //
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Locker_TryLock(t *testing.T) {
|
||||
func Test_Locker_TryLockFunc(t *testing.T) {
|
||||
//no expire
|
||||
gtest.Case(t, func() {
|
||||
key := "testTryLock"
|
||||
key := "testTryLockFunc"
|
||||
array := garray.New()
|
||||
go func() {
|
||||
if gmlock.TryLock(key, 200*time.Millisecond) {
|
||||
gmlock.TryLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if !gmlock.TryLock(key) {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gmlock.TryLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
} else {
|
||||
gmlock.Unlock(key)
|
||||
}
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if gmlock.TryLock(key) {
|
||||
time.Sleep(70 * time.Millisecond)
|
||||
gmlock.TryLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
gmlock.Unlock(key)
|
||||
}
|
||||
})
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
time.Sleep(350 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ func Test_Locker_TryRLock(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Locker_RLockFunc(t *testing.T) {
|
||||
func Test_Locker_RLockFunc1(t *testing.T) {
|
||||
//RLockFunc before Lock
|
||||
gtest.Case(t, func() {
|
||||
key := "testRLockFuncBeforeLock"
|
||||
@ -155,7 +155,7 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
||||
go func() {
|
||||
gmlock.RLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
@ -165,9 +165,10 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
||||
array.Append(1)
|
||||
gmlock.Unlock(key)
|
||||
}()
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
|
||||
@ -193,6 +194,10 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_Locker_RLockFunc2(t *testing.T) {
|
||||
|
||||
//Lock before RLockFuncs
|
||||
gtest.Case(t, func() {
|
||||
key := "testLockBeforeRLockFuncs"
|
||||
@ -200,26 +205,29 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
||||
go func() {
|
||||
gmlock.Lock(key)
|
||||
array.Append(1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
//glog.Println("add1")
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
gmlock.Unlock(key)
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gmlock.RLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(70 * time.Millisecond)
|
||||
//glog.Println("add2")
|
||||
time.Sleep(700 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gmlock.RLockFunc(key, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(70 * time.Millisecond)
|
||||
//glog.Println("add3")
|
||||
time.Sleep(700 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(70 * time.Millisecond)
|
||||
time.Sleep(700 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
@ -1,37 +1,35 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmlock
|
||||
// Package gmutex implements graceful concurrent-safe mutex with more rich features.
|
||||
package gmutex
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"math"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// The high level RWMutex.
|
||||
// It wraps the sync.RWMutex to implements more rich features.
|
||||
// The high level Mutex, which implements more rich features for mutex.
|
||||
type Mutex struct {
|
||||
mu sync.RWMutex
|
||||
wid *gtype.Int64 // Unique id, used for multiple and safe logic Unlock.
|
||||
locking *gtype.Bool // Locking mark for atomic operation for *Lock and Try*Lock functions.
|
||||
// There must be only one locking operation at the same time for concurrent safe purpose.
|
||||
state *gtype.Int32 // Locking state:
|
||||
// 0: writing lock false;
|
||||
// -1: writing lock true;
|
||||
// >=1: reading lock;
|
||||
state *gtype.Int32 // Indicates the state of mutex.
|
||||
writer *gtype.Int32 // Pending writer count.
|
||||
reader *gtype.Int32 // Pending reader count.
|
||||
writing chan struct{} // Channel used for writer blocking.
|
||||
reading chan struct{} // Channel used for reader blocking.
|
||||
}
|
||||
|
||||
// NewMutex creates and returns a new mutex.
|
||||
func NewMutex() *Mutex {
|
||||
// New creates and returns a new mutex.
|
||||
func New() *Mutex {
|
||||
return &Mutex{
|
||||
wid: gtype.NewInt64(),
|
||||
state: gtype.NewInt32(),
|
||||
locking: gtype.NewBool(),
|
||||
writer: gtype.NewInt32(),
|
||||
reader: gtype.NewInt32(),
|
||||
writing: make(chan struct{}, 1),
|
||||
reading: make(chan struct{}, math.MaxInt32),
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,15 +37,15 @@ func NewMutex() *Mutex {
|
||||
// If the lock is already locked for reading or writing,
|
||||
// Lock blocks until the lock is available.
|
||||
func (m *Mutex) Lock() {
|
||||
if m.locking.Cas(false, true) {
|
||||
m.mu.Lock()
|
||||
// State should be changed after locks.
|
||||
m.state.Set(-1)
|
||||
m.wid.Add(1)
|
||||
m.locking.Set(false)
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
m.Lock()
|
||||
for {
|
||||
// If there're no readers pending and writing lock currently,
|
||||
// then do the writing lock checks.
|
||||
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
|
||||
return
|
||||
}
|
||||
m.writer.Add(1)
|
||||
<-m.writing
|
||||
m.writer.Add(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +53,17 @@ func (m *Mutex) Lock() {
|
||||
// It is safe to be called multiple times if there's any locks or not.
|
||||
func (m *Mutex) Unlock() {
|
||||
if m.state.Cas(-1, 0) {
|
||||
m.mu.Unlock()
|
||||
// Writing lock unlocks, then first check the blocked readers.
|
||||
if n := m.reader.Val(); n > 0 {
|
||||
// If there're readers blocked, unlock them with preemption.
|
||||
for ; n > 0; n-- {
|
||||
m.reading <- struct{}{}
|
||||
}
|
||||
return
|
||||
}
|
||||
if m.writer.Val() > 0 {
|
||||
m.writing <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,14 +71,8 @@ func (m *Mutex) Unlock() {
|
||||
// It returns true if success, or if there's a write/reading lock on the mutex,
|
||||
// it returns false.
|
||||
func (m *Mutex) TryLock() bool {
|
||||
if m.locking.Cas(false, true) {
|
||||
if m.state.Cas(0, -1) {
|
||||
m.mu.Lock()
|
||||
m.wid.Add(1)
|
||||
m.locking.Set(false)
|
||||
return true
|
||||
}
|
||||
m.locking.Set(false)
|
||||
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -79,59 +81,106 @@ func (m *Mutex) TryLock() bool {
|
||||
// If the mutex is already locked for writing,
|
||||
// It blocks until the lock is available.
|
||||
func (m *Mutex) RLock() {
|
||||
if m.locking.Cas(false, true) {
|
||||
m.mu.RLock()
|
||||
// State should be changed after locks.
|
||||
m.state.Add(1)
|
||||
m.locking.Set(false)
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
m.RLock()
|
||||
var n int32
|
||||
for {
|
||||
// If there're no writing lock and pending writers currently,
|
||||
// then do the reading lock checks.
|
||||
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
|
||||
if m.state.Cas(n, n+1) {
|
||||
return
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Or else pending the reader.
|
||||
m.reader.Add(1)
|
||||
<-m.reading
|
||||
m.reader.Add(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// RUnlock unlocks the reading lock.
|
||||
// It is safe to be called multiple times if there's any locks or not.
|
||||
func (m *Mutex) RUnlock() {
|
||||
if n := m.state.Val(); n >= 1 {
|
||||
if m.state.Cas(n, n-1) {
|
||||
m.mu.RUnlock()
|
||||
var n int32
|
||||
for {
|
||||
if n = m.state.Val(); n >= 1 {
|
||||
if m.state.Cas(n, n-1) {
|
||||
break
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
}
|
||||
} else {
|
||||
m.RUnlock()
|
||||
break
|
||||
}
|
||||
}
|
||||
// Reading lock unlocks, then first check the blocked writers.
|
||||
if n == 1 && m.writer.Val() > 0 {
|
||||
// No readers blocked, then the writers can take place.
|
||||
m.writing <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// TryRLock tries locking the mutex for reading.
|
||||
// It returns true if success, or if there's a writing lock on the mutex, it returns false.
|
||||
func (m *Mutex) TryRLock() bool {
|
||||
if m.locking.Cas(false, true) {
|
||||
if m.state.Val() >= 0 {
|
||||
m.mu.RLock()
|
||||
m.state.Add(1)
|
||||
m.locking.Set(false)
|
||||
return true
|
||||
var n int32
|
||||
for {
|
||||
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
|
||||
if m.state.Cas(n, n+1) {
|
||||
return true
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
m.locking.Set(false)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLocked checks whether the mutex is locked by writing or reading lock.
|
||||
// IsLocked checks whether the mutex is locked with writing or reading lock.
|
||||
// Note that the result might be changed after it's called,
|
||||
// so it cannot be the criterion for atomic operations.
|
||||
func (m *Mutex) IsLocked() bool {
|
||||
return m.state.Val() != 0
|
||||
}
|
||||
|
||||
// IsRLocked checks whether the mutex is locked by writing lock.
|
||||
// IsWLocked checks whether the mutex is locked by writing lock.
|
||||
// Note that the result might be changed after it's called,
|
||||
// so it cannot be the criterion for atomic operations.
|
||||
func (m *Mutex) IsWLocked() bool {
|
||||
return m.state.Val() < 0
|
||||
}
|
||||
|
||||
// IsRLocked checks whether the mutex is locked by reading lock.
|
||||
// Note that the result might be changed after it's called,
|
||||
// so it cannot be the criterion for atomic operations.
|
||||
func (m *Mutex) IsRLocked() bool {
|
||||
return m.state.Val() > 0
|
||||
}
|
||||
|
||||
// LockFunc locks the mutex for writing with given callback function <f>.
|
||||
// If there's a write/reading lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) LockFunc(f func()) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
f()
|
||||
}
|
||||
|
||||
// RLockFunc locks the mutex for reading with given callback function <f>.
|
||||
// If there's a writing lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) RLockFunc(f func()) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
f()
|
||||
}
|
||||
|
||||
// TryLockFunc tries locking the mutex for writing with given callback function <f>.
|
||||
// it returns true if success, or if there's a write/reading lock on the mutex,
|
||||
// it returns false.
|
||||
@ -158,23 +207,3 @@ func (m *Mutex) TryRLockFunc(f func()) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LockFunc locks the mutex for writing with given callback function <f>.
|
||||
// If there's a write/reading lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) LockFunc(f func()) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
f()
|
||||
}
|
||||
|
||||
// RLockFunc locks the mutex for reading with given callback function <f>.
|
||||
// If there's a writing lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) RLockFunc(f func()) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
f()
|
||||
}
|
67
g/os/gmutex/gmutex_bench_test.go
Normal file
67
g/os/gmutex/gmutex_bench_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmutex_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/os/gmutex"
|
||||
)
|
||||
|
||||
var (
|
||||
mu = sync.Mutex{}
|
||||
rwmu = sync.RWMutex{}
|
||||
gmu = gmutex.New()
|
||||
)
|
||||
|
||||
func Benchmark_Mutex_LockUnlock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
mu.Lock()
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_RWMutex_LockUnlock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rwmu.Lock()
|
||||
rwmu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_RWMutex_RLockRUnlock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rwmu.RLock()
|
||||
rwmu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_GMutex_LockUnlock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmu.Lock()
|
||||
gmu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_GMutex_TryLock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmu.TryLock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_GMutex_RLockRUnlock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmu.RLock()
|
||||
gmu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_GMutex_TryRLock(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmu.TryRLock()
|
||||
}
|
||||
}
|
258
g/os/gmutex/gmutex_uinit_test.go
Normal file
258
g/os/gmutex/gmutex_uinit_test.go
Normal file
@ -0,0 +1,258 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmutex_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/os/gmutex"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Mutex_RUnlock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
for index := 0; index < 1000; index++ {
|
||||
go func() {
|
||||
mu.RLockFunc(func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gtest.Assert(mu.IsRLocked(), true)
|
||||
gtest.Assert(mu.IsLocked(), true)
|
||||
gtest.Assert(mu.IsWLocked(), false)
|
||||
for index := 0; index < 1000; index++ {
|
||||
go func() {
|
||||
mu.RUnlock()
|
||||
}()
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
gtest.Assert(mu.IsRLocked(), false)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_IsLocked(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gtest.Assert(mu.IsLocked(), true)
|
||||
gtest.Assert(mu.IsWLocked(), true)
|
||||
gtest.Assert(mu.IsRLocked(), false)
|
||||
time.Sleep(110 * time.Millisecond)
|
||||
gtest.Assert(mu.IsLocked(), false)
|
||||
gtest.Assert(mu.IsWLocked(), false)
|
||||
|
||||
go func() {
|
||||
mu.RLockFunc(func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
gtest.Assert(mu.IsRLocked(), true)
|
||||
gtest.Assert(mu.IsLocked(), true)
|
||||
gtest.Assert(mu.IsWLocked(), false)
|
||||
time.Sleep(110 * time.Millisecond)
|
||||
gtest.Assert(mu.IsRLocked(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_Unlock(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(60 * time.Millisecond)
|
||||
mu.Unlock()
|
||||
mu.Unlock()
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_TryLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.TryLockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(110 * time.Millisecond)
|
||||
mu.TryLockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.RLockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.RLockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.RLockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
mu.RLockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
gtest.Assert(array.Len(), 0)
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Mutex_TryRLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
mu := gmutex.New()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(700 * time.Millisecond)
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(700 * time.Millisecond)
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
})
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 3)
|
||||
})
|
||||
}
|
@ -8,6 +8,8 @@
|
||||
package grpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
)
|
||||
@ -41,35 +43,46 @@ func New(limit ...int) *Pool {
|
||||
|
||||
// Add pushes a new job to the pool using default goroutine pool.
|
||||
// The job will be executed asynchronously.
|
||||
func Add(f func()) {
|
||||
pool.Add(f)
|
||||
func Add(f func()) error {
|
||||
return pool.Add(f)
|
||||
}
|
||||
|
||||
// Size returns current goroutine count of default goroutine pool.
|
||||
func Size() int {
|
||||
return pool.count.Val()
|
||||
return pool.Size()
|
||||
}
|
||||
|
||||
// Jobs returns current job count of default goroutine pool.
|
||||
func Jobs() int {
|
||||
return pool.list.Len()
|
||||
return pool.Jobs()
|
||||
}
|
||||
|
||||
// Add pushes a new job to the pool.
|
||||
// The job will be executed asynchronously.
|
||||
func (p *Pool) Add(f func()) {
|
||||
func (p *Pool) Add(f func()) error {
|
||||
for p.closed.Val() {
|
||||
return errors.New("pool closed")
|
||||
}
|
||||
p.list.PushFront(f)
|
||||
// check whether to create a new goroutine or not.
|
||||
if p.count.Val() == p.limit {
|
||||
return
|
||||
var n int
|
||||
for {
|
||||
n = p.count.Val()
|
||||
if p.limit != -1 && n >= p.limit {
|
||||
return nil
|
||||
}
|
||||
if p.count.Cas(n, n+1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// ensure atomicity.
|
||||
if p.limit != -1 && p.count.Add(1) > p.limit {
|
||||
p.count.Add(-1)
|
||||
return
|
||||
}
|
||||
// fork a new goroutine to consume the job list.
|
||||
p.fork()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the pool.
|
||||
// This capacity is defined when pool is created.
|
||||
// If it returns -1 means no limit.
|
||||
func (p *Pool) Cap() int {
|
||||
return p.limit
|
||||
}
|
||||
|
||||
// Size returns current goroutine count of the pool.
|
||||
@ -97,6 +110,11 @@ func (p *Pool) fork() {
|
||||
}()
|
||||
}
|
||||
|
||||
// IsClosed returns if pool is closed.
|
||||
func (p *Pool) IsClosed() bool {
|
||||
return p.closed.Val()
|
||||
}
|
||||
|
||||
// Close closes the goroutine pool, which makes all goroutines exit.
|
||||
func (p *Pool) Close() {
|
||||
p.closed.Set(true)
|
||||
|
@ -7,12 +7,14 @@
|
||||
package gtest_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func TestCase(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(1, 1)
|
||||
gtest.AssertNE(1, 0)
|
||||
})
|
||||
}
|
||||
|
@ -9,9 +9,10 @@
|
||||
package gregex_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
var pattern = `(\w+).+\-\-\s*(.+)`
|
||||
|
1368
g/util/gconv/gconv_z_all_test.go
Normal file
1368
g/util/gconv/gconv_z_all_test.go
Normal file
File diff suppressed because it is too large
Load Diff
25
geg/net/ghttp/server/log/log.go
Normal file
25
geg/net/ghttp/server/log/log.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.BindHandler("/log/handler", func(r *ghttp.Request) {
|
||||
r.Response.WriteStatus(http.StatusNotFound, "文件找不到了")
|
||||
})
|
||||
s.SetAccessLogEnabled(true)
|
||||
s.SetErrorLogEnabled(true)
|
||||
//s.SetLogHandler(func(r *ghttp.Request, error ...interface{}) {
|
||||
// if len(error) > 0 {
|
||||
// // 如果是错误日志
|
||||
// fmt.Println("错误产生了:", error[0])
|
||||
// }
|
||||
// // 这里是请求日志
|
||||
// fmt.Println("请求处理完成,请求地址:", r.URL.String(), "请求结果:", r.Response.Status)
|
||||
//})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
17
geg/net/ghttp/server/log/log_error.go
Normal file
17
geg/net/ghttp/server/log/log_error.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.BindHandler("/log/error", func(r *ghttp.Request) {
|
||||
if j := r.GetJson(); j != nil {
|
||||
r.Response.Write(j.Get("test"))
|
||||
}
|
||||
})
|
||||
s.SetErrorLogEnabled(true)
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
@ -2,8 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -1,17 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/os/gflock"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gproc"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := gflock.New("demo.lock")
|
||||
l.Lock()
|
||||
glog.Printf("locked by pid: %d", gproc.Pid())
|
||||
time.Sleep(3 * time.Second)
|
||||
time.Sleep(10 * time.Second)
|
||||
l.UnLock()
|
||||
glog.Printf("unlocked by pid: %d", gproc.Pid())
|
||||
}
|
||||
|
@ -1,53 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/os/gmlock"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mu := gmlock.NewMutex()
|
||||
array := garray.New()
|
||||
go func() {
|
||||
mu.LockFunc(func() {
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fmt.Println("====unlock")
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
fmt.Println("tryRLock1")
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
fmt.Println("tryRLock1 success")
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
fmt.Println("tryRLock2")
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
fmt.Println("tryRLock2 success")
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
fmt.Println("tryRLock3")
|
||||
mu.TryRLockFunc(func() {
|
||||
array.Append(1)
|
||||
fmt.Println("tryRLock3 success")
|
||||
})
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
fmt.Println("====array len:", array.Len())
|
||||
gtest.Assert(array.Len(), 3)
|
||||
type Order struct{}
|
||||
|
||||
func (order *Order) Get(r *ghttp.Request) {
|
||||
r.Response.Write("GET")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc{
|
||||
"BeforeServe": func(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
},
|
||||
})
|
||||
s.BindObjectRest("/api.v1/{.struct}", new(Order))
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
@ -1,14 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gmlock"
|
||||
"time"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
key := "test3"
|
||||
gmlock.Lock(key, 200*time.Millisecond)
|
||||
fmt.Println("TryLock:", gmlock.TryLock(key))
|
||||
fmt.Println("TryLock:", gmlock.TryLock(key))
|
||||
type Schedule struct{}
|
||||
|
||||
type Task struct{}
|
||||
|
||||
func (c *Schedule) ListDir(r *ghttp.Request) {
|
||||
r.Response.Writeln("ListDir")
|
||||
}
|
||||
|
||||
func (c *Task) Add(r *ghttp.Request) {
|
||||
r.Response.Writeln("Add")
|
||||
}
|
||||
|
||||
func (c *Task) Task(r *ghttp.Request) {
|
||||
r.Response.Writeln("Task")
|
||||
}
|
||||
|
||||
// 实现权限校验
|
||||
// 通过事件回调,类似于中间件机制,但是可控制的粒度更细,可以精准注册到路由规则
|
||||
func AuthHookHandler(r *ghttp.Request) {
|
||||
// 如果权限校验失败,调用 r.ExitAll() 退出执行流程
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/schedule").Bind([]ghttp.GroupItem{
|
||||
{"ALL", "*", AuthHookHandler, ghttp.HOOK_BEFORE_SERVE},
|
||||
{"POST", "/schedule", new(Schedule)},
|
||||
{"POST", "/task", new(Task)},
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user