Merge branch 'master' into 完善garray测试

This commit is contained in:
jroam 2019-06-25 09:34:17 +08:00
commit 3bfff2347f
53 changed files with 2759 additions and 719 deletions

1
.gitignore vendored
View File

@ -7,7 +7,6 @@
.settings/
.vscode/
vender/
log/
composer.lock
gitpush.sh
pkg/

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)
}

View File

@ -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() {

View File

@ -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)
}

View File

@ -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"
)

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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>.

View File

@ -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...)
}

View File

@ -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
View File

@ -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

View File

@ -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...)
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -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)

View File

@ -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) {

View 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
}

View 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")
})
}

View File

@ -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,

View File

@ -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异步运行的场景

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
})

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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()
}

View 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()
}
}

View 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)
})
}

View File

@ -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)

View File

@ -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)
})
}

View File

@ -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*(.+)`

File diff suppressed because it is too large Load Diff

View 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()
}

View 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()
}

View File

@ -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() {

View File

@ -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())
}

View File

@ -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()
}

View File

@ -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()
}

2
go.mod
View File

@ -1 +1 @@
module github.com/gogf/gf
module github.com/gogf/gf