mirror of
https://gitee.com/johng/gf.git
synced 2024-12-03 04:37:49 +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/
|
.settings/
|
||||||
.vscode/
|
.vscode/
|
||||||
vender/
|
vender/
|
||||||
log/
|
|
||||||
composer.lock
|
composer.lock
|
||||||
gitpush.sh
|
gitpush.sh
|
||||||
pkg/
|
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)
|
[![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)
|
[![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)
|
[![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)
|
[![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)
|
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
|
||||||
|
@ -9,11 +9,12 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/g/util/grand"
|
"github.com/gogf/gf/g/util/grand"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IntArray struct {
|
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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]int)(nil)
|
array := ([]int)(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]int, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]int, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// See PushRight.
|
||||||
func (a *IntArray) Append(value ...int) *IntArray {
|
func (a *IntArray) Append(value ...int) *IntArray {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
@ -488,27 +541,6 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
|
|||||||
return a
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *IntArray) Rand() int {
|
func (a *IntArray) Rand() int {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -9,11 +9,12 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/g/util/grand"
|
"github.com/gogf/gf/g/util/grand"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Array struct {
|
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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]interface{})(nil)
|
array := ([]interface{})(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]interface{}, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]interface{}, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// See PushRight.
|
||||||
func (a *Array) Append(value ...interface{}) *Array {
|
func (a *Array) Append(value ...interface{}) *Array {
|
||||||
a.PushRight(value...)
|
a.PushRight(value...)
|
||||||
@ -482,27 +535,6 @@ func (a *Array) Pad(size int, val interface{}) *Array {
|
|||||||
return a
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *Array) Rand() interface{} {
|
func (a *Array) Rand() interface{} {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -9,12 +9,13 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
|
||||||
"github.com/gogf/gf/g/util/grand"
|
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"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 {
|
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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]string)(nil)
|
array := ([]string)(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]string, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]string, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// See PushRight.
|
||||||
func (a *StringArray) Append(value ...string) *StringArray {
|
func (a *StringArray) Append(value ...string) *StringArray {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
@ -488,27 +541,6 @@ func (a *StringArray) Pad(size int, value string) *StringArray {
|
|||||||
return a
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *StringArray) Rand() string {
|
func (a *StringArray) Rand() string {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -9,12 +9,13 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/g/util/grand"
|
"github.com/gogf/gf/g/util/grand"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// It's using increasing order in default.
|
// 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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]int)(nil)
|
array := ([]int)(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]int, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]int, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// Len returns the length of array.
|
||||||
func (a *SortedIntArray) Len() int {
|
func (a *SortedIntArray) Len() int {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
@ -433,27 +486,6 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
|
|||||||
return n
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *SortedIntArray) Rand() int {
|
func (a *SortedIntArray) Rand() int {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -9,12 +9,13 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/g/util/grand"
|
"github.com/gogf/gf/g/util/grand"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// It's using increasing order in default.
|
// 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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]interface{})(nil)
|
array := ([]interface{})(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]interface{}, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]interface{}, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// Sum returns the sum of values in an array.
|
||||||
func (a *SortedArray) Sum() (sum int) {
|
func (a *SortedArray) Sum() (sum int) {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
@ -434,27 +487,6 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
|
|||||||
return n
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *SortedArray) Rand() interface{} {
|
func (a *SortedArray) Rand() interface{} {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -9,13 +9,14 @@ package garray
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/g/util/grand"
|
"github.com/gogf/gf/g/util/grand"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// It's using increasing order in default.
|
// 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].
|
// Range picks and returns items by range, like array[start:end].
|
||||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||||
// else a pointer to the underlying data.
|
// 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()
|
a.mu.RLock()
|
||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
length := len(a.array)
|
offsetEnd := len(a.array)
|
||||||
if start > length || start > end {
|
if len(end) > 0 && end[0] < offsetEnd {
|
||||||
|
offsetEnd = end[0]
|
||||||
|
}
|
||||||
|
if start > offsetEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
if end > length {
|
|
||||||
end = length
|
|
||||||
}
|
|
||||||
array := ([]string)(nil)
|
array := ([]string)(nil)
|
||||||
if a.mu.IsSafe() {
|
if a.mu.IsSafe() {
|
||||||
a.mu.RLock()
|
array = make([]string, offsetEnd-start)
|
||||||
defer a.mu.RUnlock()
|
copy(array, a.array[start:offsetEnd])
|
||||||
array = make([]string, end-start)
|
|
||||||
copy(array, a.array[start:end])
|
|
||||||
} else {
|
} else {
|
||||||
array = a.array[start:end]
|
array = a.array[start:offsetEnd]
|
||||||
}
|
}
|
||||||
return array
|
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.
|
// Sum returns the sum of values in an array.
|
||||||
func (a *SortedStringArray) Sum() (sum int) {
|
func (a *SortedStringArray) Sum() (sum int) {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
@ -428,27 +481,6 @@ func (a *SortedStringArray) Chunk(size int) [][]string {
|
|||||||
return n
|
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).
|
// Rand randomly returns one item from array(no deleting).
|
||||||
func (a *SortedStringArray) Rand() string {
|
func (a *SortedStringArray) Rand() string {
|
||||||
a.mu.RLock()
|
a.mu.RLock()
|
||||||
|
@ -11,10 +11,7 @@ package garray_test
|
|||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/container/garray"
|
"github.com/gogf/gf/g/container/garray"
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_IntArray_Basic(t *testing.T) {
|
func Test_IntArray_Basic(t *testing.T) {
|
||||||
@ -49,7 +46,7 @@ func TestIntArray_Sort(t *testing.T) {
|
|||||||
expect1 := []int{0, 1, 2, 3}
|
expect1 := []int{0, 1, 2, 3}
|
||||||
expect2 := []int{3, 2, 1, 0}
|
expect2 := []int{3, 2, 1, 0}
|
||||||
array := garray.NewIntArray()
|
array := garray.NewIntArray()
|
||||||
array2:=garray.NewIntArray(true)
|
array2 := garray.NewIntArray(true)
|
||||||
for i := 3; i >= 0; i-- {
|
for i := 3; i >= 0; i-- {
|
||||||
array.Append(i)
|
array.Append(i)
|
||||||
array2.Append(i)
|
array2.Append(i)
|
||||||
@ -62,7 +59,6 @@ func TestIntArray_Sort(t *testing.T) {
|
|||||||
array2.Sort(true)
|
array2.Sort(true)
|
||||||
gtest.Assert(array2.Slice(), expect2)
|
gtest.Assert(array2.Slice(), expect2)
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +106,11 @@ func TestIntArray_Range(t *testing.T) {
|
|||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||||
array1 := garray.NewIntArrayFrom(value1)
|
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(0, 1), []int{0})
|
||||||
gtest.Assert(array1.Range(1, 2), []int{1})
|
gtest.Assert(array1.Range(1, 2), []int{1})
|
||||||
gtest.Assert(array1.Range(0, 2), []int{0, 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(-1, 10), value1)
|
||||||
gtest.Assert(array1.Range(8, 2), nil)
|
gtest.Assert(array1.Range(8, 2), nil)
|
||||||
|
|
||||||
@ -124,32 +121,32 @@ func TestIntArray_Range(t *testing.T) {
|
|||||||
func TestIntArray_Merge(t *testing.T) {
|
func TestIntArray_Merge(t *testing.T) {
|
||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
n1 := []int{1, 2, 4, 3}
|
n1 := []int{1, 2, 4, 3}
|
||||||
n2:=[]int{7,8,9}
|
n2 := []int{7, 8, 9}
|
||||||
n3:=[]int{3,6}
|
n3 := []int{3, 6}
|
||||||
|
|
||||||
s1:=[]string{"a","b","c"}
|
s1 := []string{"a", "b", "c"}
|
||||||
in1:=[]interface{}{1,"a",2,"b"}
|
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))
|
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
a1 := garray.NewIntArrayFrom(n1)
|
a1 := garray.NewIntArrayFrom(n1)
|
||||||
b1 := garray.NewStringArrayFrom(s1)
|
b1 := garray.NewStringArrayFrom(s1)
|
||||||
b2:=garray.NewIntArrayFrom(n3)
|
b2 := garray.NewIntArrayFrom(n3)
|
||||||
b3:=garray.NewArrayFrom(in1)
|
b3 := garray.NewArrayFrom(in1)
|
||||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||||
b6:=garray.NewSortedArrayFrom(in1,func1)
|
b6 := garray.NewSortedArrayFrom(in1, func1)
|
||||||
|
|
||||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||||
gtest.Assert(a1.Merge(b6).Len(),27)
|
gtest.Assert(a1.Merge(b6).Len(), 27)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,10 +189,21 @@ func TestIntArray_SubSlice(t *testing.T) {
|
|||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||||
array1 := garray.NewIntArrayFrom(a1)
|
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(0, 2), []int{0, 1})
|
||||||
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
|
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
|
||||||
gtest.Assert(array1.SubSlice(5, 8), []int{5, 6})
|
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() {
|
gtest.Case(t, func() {
|
||||||
a1 := []int{1, 3, 5, 2, 6, 7}
|
a1 := []int{1, 3, 5, 2, 6, 7}
|
||||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||||
array2 := garray.NewSortedIntArrayFrom(a1,true)
|
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||||
ns1 := array1.Range(1, 4)
|
ns1 := array1.Range(1, 4)
|
||||||
gtest.Assert(len(ns1), 3)
|
gtest.Assert(len(ns1), 3)
|
||||||
gtest.Assert(ns1, []int{2, 3, 5})
|
gtest.Assert(ns1, []int{2, 3, 5})
|
||||||
@ -482,7 +490,6 @@ func TestSortedIntArray_Chunk(t *testing.T) {
|
|||||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||||
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
|
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
|
||||||
ns2 := array1.Chunk(-1)
|
ns2 := array1.Chunk(-1)
|
||||||
t.Log(ns1)
|
|
||||||
gtest.Assert(len(ns1), 3)
|
gtest.Assert(len(ns1), 3)
|
||||||
gtest.Assert(ns1[0], []int{1, 2})
|
gtest.Assert(ns1[0], []int{1, 2})
|
||||||
gtest.Assert(ns1[2], []int{5})
|
gtest.Assert(ns1[2], []int{5})
|
||||||
@ -711,37 +718,34 @@ func TestSortedIntArray_RLockFunc(t *testing.T) {
|
|||||||
gtest.Assert(a1.Contains(7), true)
|
gtest.Assert(a1.Contains(7), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestSortedIntArray_Merge(t *testing.T) {
|
func TestSortedIntArray_Merge(t *testing.T) {
|
||||||
n1 := []int{1, 2, 4, 3}
|
n1 := []int{1, 2, 4, 3}
|
||||||
n2:=[]int{7,8,9}
|
n2 := []int{7, 8, 9}
|
||||||
n3:=[]int{3,6}
|
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"}
|
||||||
|
|
||||||
a1 := garray.NewSortedIntArrayFrom(n1)
|
a1 := garray.NewSortedIntArrayFrom(n1)
|
||||||
b1 := garray.NewStringArrayFrom(s1)
|
b1 := garray.NewStringArrayFrom(s1)
|
||||||
b2:=garray.NewIntArrayFrom(n3)
|
b2 := garray.NewIntArrayFrom(n3)
|
||||||
b3:=garray.NewArrayFrom(in1)
|
b3 := garray.NewArrayFrom(in1)
|
||||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||||
|
|
||||||
|
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSortedArray_LockFunc(t *testing.T) {
|
func TestSortedArray_LockFunc(t *testing.T) {
|
||||||
n1 := []interface{}{1, 2, 4, 3}
|
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))
|
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||||
}
|
}
|
||||||
a1 := garray.NewSortedArrayFrom(n1, func1)
|
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||||
@ -769,7 +773,7 @@ func TestSortedArray_LockFunc(t *testing.T) {
|
|||||||
func TestSortedArray_RLockFunc(t *testing.T) {
|
func TestSortedArray_RLockFunc(t *testing.T) {
|
||||||
n1 := []interface{}{1, 2, 4, 3}
|
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))
|
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||||
}
|
}
|
||||||
a1 := garray.NewSortedArrayFrom(n1, func1)
|
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||||
@ -795,56 +799,54 @@ func TestSortedArray_RLockFunc(t *testing.T) {
|
|||||||
gtest.Assert(a1.Contains(7), true)
|
gtest.Assert(a1.Contains(7), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func TestSortedArray_Merge(t *testing.T) {
|
func TestSortedArray_Merge(t *testing.T) {
|
||||||
n1 := []interface{}{1, 2, 4, 3}
|
n1 := []interface{}{1, 2, 4, 3}
|
||||||
n2:=[]int{7,8,9}
|
n2 := []int{7, 8, 9}
|
||||||
n3:=[]int{3,6}
|
n3 := []int{3, 6}
|
||||||
|
|
||||||
s1:=[]string{"a","b","c"}
|
s1 := []string{"a", "b", "c"}
|
||||||
in1:=[]interface{}{1,"a",2,"b"}
|
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))
|
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
a1 := garray.NewSortedArrayFrom(n1,func1)
|
a1 := garray.NewSortedArrayFrom(n1, func1)
|
||||||
b1 := garray.NewStringArrayFrom(s1)
|
b1 := garray.NewStringArrayFrom(s1)
|
||||||
b2:=garray.NewIntArrayFrom(n3)
|
b2 := garray.NewIntArrayFrom(n3)
|
||||||
b3:=garray.NewArrayFrom(in1)
|
b3 := garray.NewArrayFrom(in1)
|
||||||
b4:=garray.NewSortedStringArrayFrom(s1)
|
b4 := garray.NewSortedStringArrayFrom(s1)
|
||||||
b5:=garray.NewSortedIntArrayFrom(n3)
|
b5 := garray.NewSortedIntArrayFrom(n3)
|
||||||
|
|
||||||
gtest.Assert(a1.Merge(n2).Len(),7)
|
gtest.Assert(a1.Merge(n2).Len(), 7)
|
||||||
gtest.Assert(a1.Merge(n3).Len(),9)
|
gtest.Assert(a1.Merge(n3).Len(), 9)
|
||||||
gtest.Assert(a1.Merge(b1).Len(),12)
|
gtest.Assert(a1.Merge(b1).Len(), 12)
|
||||||
gtest.Assert(a1.Merge(b2).Len(),14)
|
gtest.Assert(a1.Merge(b2).Len(), 14)
|
||||||
gtest.Assert(a1.Merge(b3).Len(),18)
|
gtest.Assert(a1.Merge(b3).Len(), 18)
|
||||||
gtest.Assert(a1.Merge(b4).Len(),21)
|
gtest.Assert(a1.Merge(b4).Len(), 21)
|
||||||
gtest.Assert(a1.Merge(b5).Len(),23)
|
gtest.Assert(a1.Merge(b5).Len(), 23)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntArray_SortFunc(t *testing.T) {
|
func TestIntArray_SortFunc(t *testing.T) {
|
||||||
n1:=[]int{1,2,3,5,4}
|
n1 := []int{1, 2, 3, 5, 4}
|
||||||
a1:=garray.NewIntArrayFrom(n1)
|
a1 := garray.NewIntArrayFrom(n1)
|
||||||
|
|
||||||
func1:=func(v1,v2 int)bool{
|
func1 := func(v1, v2 int) bool {
|
||||||
if v1>v2{
|
if v1 > v2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
func2:=func(v1,v2 int)bool{
|
func2 := func(v1, v2 int) bool {
|
||||||
if v1>v2{
|
if v1 > v2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
a2:=a1.SortFunc(func1)
|
a2 := a1.SortFunc(func1)
|
||||||
gtest.Assert(a2,[]int{1,2,3,4,5})
|
gtest.Assert(a2, []int{1, 2, 3, 4, 5})
|
||||||
a3:=a1.SortFunc(func2)
|
a3 := a1.SortFunc(func2)
|
||||||
gtest.Assert(a3,[]int{5,4,3,2,1})
|
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.AssertLT(t2-t1, 20)
|
||||||
gtest.Assert(a1.Contains(7), true)
|
gtest.Assert(a1.Contains(7), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,12 @@ package gpool
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/glist"
|
"github.com/gogf/gf/g/container/glist"
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
"github.com/gogf/gf/g/os/gtime"
|
"github.com/gogf/gf/g/os/gtime"
|
||||||
"github.com/gogf/gf/g/os/gtimer"
|
"github.com/gogf/gf/g/os/gtimer"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Object-Reusable Pool.
|
// Object-Reusable Pool.
|
||||||
@ -126,6 +127,7 @@ func (p *Pool) checkExpire() {
|
|||||||
gtimer.Exit()
|
gtimer.Exit()
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
// TODO Do not use Pop and Push mechanism, which is not graceful.
|
||||||
if r := p.list.PopFront(); r != nil {
|
if r := p.list.PopFront(); r != nil {
|
||||||
item := r.(*poolItem)
|
item := r.(*poolItem)
|
||||||
if item.expire == 0 || item.expire > gtime.Millisecond() {
|
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.
|
// 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)
|
return atomic.AddInt64(&v.value, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/gogf/gf/g/crypto/gmd5"
|
"github.com/gogf/gf/g/crypto/gmd5"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/crypto/gcrc32"
|
"github.com/gogf/gf/g/crypto/gcrc32"
|
||||||
|
"github.com/gogf/gf/g/crypto/gmd5"
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gogf/gf/g/internal/errors"
|
||||||
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encrypt encrypts any type of variable using MD5 algorithms.
|
// Encrypt encrypts any type of variable using MD5 algorithms.
|
||||||
@ -40,9 +40,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := f.Close(); e != nil {
|
err = errors.Wrap(f.Close(), "file closing error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
_, err = io.Copy(h, f)
|
_, err = io.Copy(h, f)
|
||||||
|
@ -10,11 +10,10 @@ package gsha1
|
|||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"github.com/gogf/gf/g/internal/errors"
|
||||||
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encrypt encrypts any type of variable using SHA1 algorithms.
|
// Encrypt encrypts any type of variable using SHA1 algorithms.
|
||||||
@ -37,9 +36,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := f.Close(); e != nil {
|
err = errors.Wrap(f.Close(), "file closing error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
_, err = io.Copy(h, f)
|
_, err = io.Copy(h, f)
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
package gdb_test
|
package gdb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/database/gdb"
|
"github.com/gogf/gf/g/database/gdb"
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Instance(t *testing.T) {
|
func Test_Instance(t *testing.T) {
|
||||||
|
@ -8,12 +8,13 @@
|
|||||||
package gjson
|
package gjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/internal/rwmutex"
|
|
||||||
"github.com/gogf/gf/g/text/gstr"
|
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/internal/rwmutex"
|
||||||
|
"github.com/gogf/gf/g/text/gstr"
|
||||||
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -26,8 +27,14 @@ type Json struct {
|
|||||||
mu *rwmutex.RWMutex
|
mu *rwmutex.RWMutex
|
||||||
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
|
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
|
||||||
c byte // Char separator('.' 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.
|
// 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>.
|
// Set <value> by <pattern>.
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
package gparser
|
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) {
|
func (p *Parser) ToXml(rootTag ...string) ([]byte, error) {
|
||||||
return p.json.ToXml(rootTag...)
|
return p.json.ToXml(rootTag...)
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,12 @@ package gins_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/frame/gins"
|
"github.com/gogf/gf/g/frame/gins"
|
||||||
"github.com/gogf/gf/g/os/gfile"
|
"github.com/gogf/gf/g/os/gfile"
|
||||||
"github.com/gogf/gf/g/os/gtime"
|
"github.com/gogf/gf/g/os/gtime"
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_View(t *testing.T) {
|
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 SliceAny = []interface{}
|
||||||
type SliceStr = []string
|
type SliceStr = []string
|
||||||
type SliceInt = []int
|
type SliceInt = []int
|
||||||
|
|
||||||
|
// Array is alias of Slice.
|
||||||
type Array = []interface{}
|
type Array = []interface{}
|
||||||
type ArrayAny = []interface{}
|
type ArrayAny = []interface{}
|
||||||
type ArrayStr = []string
|
type ArrayStr = []string
|
||||||
|
@ -39,7 +39,7 @@ func Throw(exception interface{}) {
|
|||||||
gutil.Throw(exception)
|
gutil.Throw(exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryCatch does the try...catch... logic.
|
// TryCatch does the try...catch... mechanism.
|
||||||
func TryCatch(try func(), catch ...func(exception interface{})) {
|
func TryCatch(try func(), catch ...func(exception interface{})) {
|
||||||
gutil.TryCatch(try, catch...)
|
gutil.TryCatch(try, catch...)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/gogf/gf/g/os/glog"
|
"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) {
|
func SetDebug(debug bool) {
|
||||||
glog.SetDebug(debug)
|
glog.SetDebug(debug)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,8 @@ func Database(name ...string) gdb.DB {
|
|||||||
return gins.Database(name...)
|
return gins.Database(name...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DB is alias of Database. See Database.
|
// DB is alias of Database.
|
||||||
|
// See Database.
|
||||||
func DB(name ...string) gdb.DB {
|
func DB(name ...string) gdb.DB {
|
||||||
return gins.Database(name...)
|
return gins.Database(name...)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ package g
|
|||||||
|
|
||||||
import "github.com/gogf/gf/g/net/ghttp"
|
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.
|
// This feature is disabled in default.
|
||||||
func SetServerGraceful(enabled bool) {
|
func SetServerGraceful(enabled bool) {
|
||||||
ghttp.SetGraceful(enabled)
|
ghttp.SetGraceful(enabled)
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
package cmdenv
|
package cmdenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Get(t *testing.T) {
|
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
|
package ghttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/text/gstr"
|
|
||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,7 +25,7 @@ type CORSOptions struct {
|
|||||||
// 默认的CORS配置
|
// 默认的CORS配置
|
||||||
func (r *Response) DefaultCORSOptions() CORSOptions {
|
func (r *Response) DefaultCORSOptions() CORSOptions {
|
||||||
return CORSOptions{
|
return CORSOptions{
|
||||||
AllowOrigin: gstr.TrimRight(r.request.Referer(), "/"),
|
AllowOrigin: "*",
|
||||||
AllowMethods: HTTP_METHODS,
|
AllowMethods: HTTP_METHODS,
|
||||||
AllowCredentials: "true",
|
AllowCredentials: "true",
|
||||||
MaxAge: 3628800,
|
MaxAge: 3628800,
|
||||||
|
@ -10,6 +10,14 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/garray"
|
"github.com/gogf/gf/g/container/garray"
|
||||||
"github.com/gogf/gf/g/container/gmap"
|
"github.com/gogf/gf/g/container/gmap"
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
@ -23,13 +31,6 @@ import (
|
|||||||
"github.com/gogf/gf/g/util/gconv"
|
"github.com/gogf/gf/g/util/gconv"
|
||||||
"github.com/gogf/gf/third/github.com/gorilla/websocket"
|
"github.com/gogf/gf/third/github.com/gorilla/websocket"
|
||||||
"github.com/gogf/gf/third/github.com/olekukonko/tablewriter"
|
"github.com/gogf/gf/third/github.com/olekukonko/tablewriter"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
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 {
|
if err := s.Start(); err != nil {
|
||||||
return err
|
glog.Fatal(err)
|
||||||
}
|
}
|
||||||
// 阻塞等待服务执行完成
|
// 阻塞等待服务执行完成
|
||||||
<-s.closeChan
|
<-s.closeChan
|
||||||
|
|
||||||
glog.Printf("%d: all servers shutdown", gproc.Pid())
|
glog.Printf("%d: all servers shutdown", gproc.Pid())
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景
|
// 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"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
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
data, err = c.Recv(length, retry...)
|
data, err = c.Recv(length, retry...)
|
||||||
return
|
return
|
||||||
@ -222,9 +222,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = c.Send(data, retry...)
|
err = c.Send(data, retry...)
|
||||||
return
|
return
|
||||||
|
@ -8,9 +8,10 @@ package gtcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -73,9 +74,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = c.SendPkg(data, option...)
|
err = c.SendPkg(data, option...)
|
||||||
return
|
return
|
||||||
@ -148,9 +147,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
data, err = c.RecvPkg(option...)
|
data, err = c.RecvPkg(option...)
|
||||||
return
|
return
|
||||||
|
@ -7,11 +7,10 @@
|
|||||||
package gtcp
|
package gtcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/gmap"
|
"github.com/gogf/gf/g/container/gmap"
|
||||||
"github.com/gogf/gf/g/container/gpool"
|
"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
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
data, err = c.Recv(length, retry...)
|
data, err = c.Recv(length, retry...)
|
||||||
return
|
return
|
||||||
@ -135,9 +132,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = c.Send(data, retry...)
|
err = c.Send(data, retry...)
|
||||||
return
|
return
|
||||||
|
@ -9,6 +9,8 @@ package gtcp
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 简单协议: (方法覆盖)发送数据
|
// 简单协议: (方法覆盖)发送数据
|
||||||
@ -46,9 +48,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
data, err = c.RecvPkg(option...)
|
data, err = c.RecvPkg(option...)
|
||||||
return
|
return
|
||||||
@ -60,9 +60,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = c.SendPkg(data, option...)
|
err = c.SendPkg(data, option...)
|
||||||
return
|
return
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 封装的UDP链接对象
|
// 封装的UDP链接对象
|
||||||
@ -181,9 +183,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetRecvDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
data, err = c.Recv(length, retry...)
|
data, err = c.Recv(length, retry...)
|
||||||
return
|
return
|
||||||
@ -195,9 +195,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := c.SetSendDeadline(time.Time{}); e != nil {
|
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
|
||||||
err = errors.New(err.Error() + "; " + e.Error())
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = c.Send(data, retry...)
|
err = c.Send(data, retry...)
|
||||||
return
|
return
|
||||||
|
@ -7,11 +7,12 @@
|
|||||||
package gcron_test
|
package gcron_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/garray"
|
"github.com/gogf/gf/g/container/garray"
|
||||||
"github.com/gogf/gf/g/os/gcron"
|
"github.com/gogf/gf/g/os/gcron"
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCron_Add_Close(t *testing.T) {
|
func TestCron_Add_Close(t *testing.T) {
|
||||||
@ -146,7 +147,7 @@ func TestCron_AddOnce2(t *testing.T) {
|
|||||||
array.Append(1)
|
array.Append(1)
|
||||||
})
|
})
|
||||||
gtest.Assert(cron.Size(), 1)
|
gtest.Assert(cron.Size(), 1)
|
||||||
time.Sleep(2500 * time.Millisecond)
|
time.Sleep(3000 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 1)
|
gtest.Assert(array.Len(), 1)
|
||||||
gtest.Assert(cron.Size(), 0)
|
gtest.Assert(cron.Size(), 0)
|
||||||
})
|
})
|
||||||
|
@ -10,13 +10,11 @@ package gflock
|
|||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/os/gfile"
|
"github.com/gogf/gf/g/os/gfile"
|
||||||
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// File locker.
|
// File locker.
|
||||||
type Locker struct {
|
type Locker struct {
|
||||||
mu sync.RWMutex // 用于外部接口调用的互斥锁(阻塞机制)
|
flock *flock.Flock // Underlying file locker.
|
||||||
flock *flock.Flock // 底层文件锁对象
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates and returns a new file locker with given <file>.
|
// 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.
|
// It returns true if success, or else returns false immediately.
|
||||||
func (l *Locker) TryLock() bool {
|
func (l *Locker) TryLock() bool {
|
||||||
ok, _ := l.flock.TryLock()
|
ok, _ := l.flock.TryLock()
|
||||||
if ok {
|
|
||||||
l.mu.Lock()
|
|
||||||
}
|
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,32 +52,61 @@ func (l *Locker) TryLock() bool {
|
|||||||
// It returns true if success, or else returns false immediately.
|
// It returns true if success, or else returns false immediately.
|
||||||
func (l *Locker) TryRLock() bool {
|
func (l *Locker) TryRLock() bool {
|
||||||
ok, _ := l.flock.TryRLock()
|
ok, _ := l.flock.TryRLock()
|
||||||
if ok {
|
|
||||||
l.mu.RLock()
|
|
||||||
}
|
|
||||||
return ok
|
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) {
|
func (l *Locker) Lock() (err error) {
|
||||||
l.mu.Lock()
|
return l.flock.Lock()
|
||||||
err = l.flock.Lock()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (l *Locker) UnLock() (err error) {
|
||||||
err = l.flock.Unlock()
|
return l.flock.Unlock()
|
||||||
l.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (l *Locker) RLock() (err error) {
|
||||||
l.mu.RLock()
|
return l.flock.RLock()
|
||||||
err = l.flock.RLock()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (l *Locker) RUnlock() (err error) {
|
||||||
err = l.flock.Unlock()
|
return l.flock.Unlock()
|
||||||
l.mu.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
@ -7,26 +7,23 @@
|
|||||||
// Package gmlock implements a concurrent-safe memory-based locker.
|
// Package gmlock implements a concurrent-safe memory-based locker.
|
||||||
package gmlock
|
package gmlock
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Default locker.
|
// Default locker.
|
||||||
locker = New()
|
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.
|
// Lock locks the <key> with writing lock.
|
||||||
// If there's a write/reading lock the <key>,
|
// If there's a write/reading lock the <key>,
|
||||||
// it will blocks until the lock is released.
|
// it will blocks until the lock is released.
|
||||||
// The parameter <expire> specifies the max duration it locks.
|
func Lock(key string) {
|
||||||
func Lock(key string, expire ...time.Duration) {
|
locker.Lock(key)
|
||||||
locker.Lock(key, expire...)
|
}
|
||||||
|
|
||||||
|
// 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>.
|
// Unlock unlocks the writing lock of the <key>.
|
||||||
@ -34,12 +31,6 @@ func Unlock(key string) {
|
|||||||
locker.Unlock(key)
|
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.
|
// RLock locks the <key> with reading lock.
|
||||||
// If there's a writing lock on <key>,
|
// If there's a writing lock on <key>,
|
||||||
// it will blocks until the writing lock is released.
|
// it will blocks until the writing lock is released.
|
||||||
@ -47,40 +38,24 @@ func RLock(key string) {
|
|||||||
locker.RLock(key)
|
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>.
|
// RUnlock unlocks the reading lock of the <key>.
|
||||||
func RUnlock(key string) {
|
func RUnlock(key string) {
|
||||||
locker.RUnlock(key)
|
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>.
|
// LockFunc locks the <key> with writing lock and callback function <f>.
|
||||||
// If there's a write/reading lock the <key>,
|
// If there's a write/reading lock the <key>,
|
||||||
// it will blocks until the lock is released.
|
// it will blocks until the lock is released.
|
||||||
//
|
//
|
||||||
// It releases the lock after <f> is executed.
|
// It releases the lock after <f> is executed.
|
||||||
//
|
func LockFunc(key string, f func()) {
|
||||||
// The parameter <expire> specifies the max duration it locks.
|
locker.LockFunc(key, f)
|
||||||
func LockFunc(key string, f func(), expire ...time.Duration) {
|
|
||||||
locker.LockFunc(key, f, expire...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RLockFunc locks the <key> with reading lock and callback function <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 will blocks until the lock is released.
|
||||||
//
|
//
|
||||||
// It releases the lock after <f> is executed.
|
// It releases the lock after <f> is executed.
|
||||||
//
|
|
||||||
// The parameter <expire> specifies the max duration it locks.
|
|
||||||
func RLockFunc(key string, f func()) {
|
func RLockFunc(key string, f func()) {
|
||||||
locker.RLockFunc(key, f)
|
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 (
|
import (
|
||||||
"github.com/gogf/gf/g/container/gmap"
|
"github.com/gogf/gf/g/container/gmap"
|
||||||
"github.com/gogf/gf/g/os/gtimer"
|
"github.com/gogf/gf/g/os/gmutex"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Memory locker.
|
// 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 {
|
type Locker struct {
|
||||||
m *gmap.StrAnyMap
|
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.
|
// Lock locks the <key> with writing lock.
|
||||||
// If there's a write/reading lock the <key>,
|
// If there's a write/reading lock the <key>,
|
||||||
// it will blocks until the lock is released.
|
// it will blocks until the lock is released.
|
||||||
// The parameter <expire> specifies the max duration it locks.
|
func (l *Locker) Lock(key string) {
|
||||||
func (l *Locker) Lock(key string, expire ...time.Duration) {
|
l.getOrNewMutex(key).Lock()
|
||||||
l.doLock(key, l.getExpire(expire...), false)
|
}
|
||||||
|
|
||||||
|
// 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>.
|
// Unlock unlocks the writing lock of the <key>.
|
||||||
func (l *Locker) Unlock(key string) {
|
func (l *Locker) Unlock(key string) {
|
||||||
if v := l.m.Get(key); v != nil {
|
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.
|
// RLock locks the <key> with reading lock.
|
||||||
// If there's a writing lock on <key>,
|
// If there's a writing lock on <key>,
|
||||||
// it will blocks until the writing lock is released.
|
// it will blocks until the writing lock is released.
|
||||||
func (l *Locker) RLock(key string) {
|
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>.
|
// RUnlock unlocks the reading lock of the <key>.
|
||||||
func (l *Locker) RUnlock(key string) {
|
func (l *Locker) RUnlock(key string) {
|
||||||
if v := l.m.Get(key); v != nil {
|
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>.
|
// 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 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.
|
// It releases the lock after <f> is executed.
|
||||||
//
|
func (l *Locker) TryLockFunc(key string, f func()) bool {
|
||||||
// The parameter <expire> specifies the max duration it locks.
|
if l.TryLock(key) {
|
||||||
func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool {
|
|
||||||
if l.TryLock(key, expire...) {
|
|
||||||
defer l.Unlock(key)
|
defer l.Unlock(key)
|
||||||
f()
|
f()
|
||||||
return true
|
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 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.
|
// 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 {
|
func (l *Locker) TryRLockFunc(key string, f func()) bool {
|
||||||
if l.TryRLock(key) {
|
if l.TryRLock(key) {
|
||||||
defer l.RUnlock(key)
|
defer l.RUnlock(key)
|
||||||
@ -97,90 +114,20 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockFunc locks the <key> with writing lock and callback function <f>.
|
// Remove removes mutex with given <key> from locker.
|
||||||
// If there's a write/reading lock the <key>,
|
func (l *Locker) Remove(key string) {
|
||||||
// it will blocks until the lock is released.
|
l.m.Remove(key)
|
||||||
//
|
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RLockFunc locks the <key> with reading lock and callback function <f>.
|
// Clear removes all mutexes from locker.
|
||||||
// If there's a writing lock the <key>,
|
func (l *Locker) Clear() {
|
||||||
// it will blocks until the lock is released.
|
l.m.Clear()
|
||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOrNewMutex returns the mutex of given <key> if it exists,
|
// getOrNewMutex returns the mutex of given <key> if it exists,
|
||||||
// or else creates and returns a new one.
|
// 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 l.m.GetOrSetFuncLock(key, func() interface{} {
|
||||||
return NewMutex()
|
return gmutex.New()
|
||||||
}).(*Mutex)
|
}).(*gmutex.Mutex)
|
||||||
}
|
}
|
||||||
|
@ -44,58 +44,61 @@ func Test_Locker_Lock(t *testing.T) {
|
|||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 4)
|
gtest.Assert(array.Len(), 4)
|
||||||
})
|
})
|
||||||
//expire
|
}
|
||||||
|
|
||||||
|
func Test_Locker_LockFunc(t *testing.T) {
|
||||||
|
//no expire
|
||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
key := "testLockExpire"
|
key := "testLockFunc"
|
||||||
array := garray.New()
|
array := garray.New()
|
||||||
go func() {
|
go func() {
|
||||||
gmlock.Lock(key, 100*time.Millisecond)
|
gmlock.LockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}) //
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
gmlock.Lock(key)
|
gmlock.LockFunc(key, func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
array.Append(1)
|
||||||
array.Append(1)
|
})
|
||||||
gmlock.Unlock(key)
|
|
||||||
}()
|
}()
|
||||||
time.Sleep(150 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 1)
|
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)
|
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() {
|
gtest.Case(t, func() {
|
||||||
key := "testTryLock"
|
key := "testTryLockFunc"
|
||||||
array := garray.New()
|
array := garray.New()
|
||||||
go func() {
|
go func() {
|
||||||
if gmlock.TryLock(key, 200*time.Millisecond) {
|
gmlock.TryLockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
}
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
})
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
if !gmlock.TryLock(key) {
|
gmlock.TryLockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
} else {
|
})
|
||||||
gmlock.Unlock(key)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(70 * time.Millisecond)
|
||||||
if gmlock.TryLock(key) {
|
gmlock.TryLockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
gmlock.Unlock(key)
|
})
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 1)
|
gtest.Assert(array.Len(), 1)
|
||||||
time.Sleep(80 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 2)
|
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
|
//RLockFunc before Lock
|
||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
key := "testRLockFuncBeforeLock"
|
key := "testRLockFuncBeforeLock"
|
||||||
@ -155,7 +155,7 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
gmlock.RLockFunc(key, func() {
|
gmlock.RLockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
@ -165,9 +165,10 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
|||||||
array.Append(1)
|
array.Append(1)
|
||||||
gmlock.Unlock(key)
|
gmlock.Unlock(key)
|
||||||
}()
|
}()
|
||||||
time.Sleep(20 * time.Millisecond)
|
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 1)
|
gtest.Assert(array.Len(), 1)
|
||||||
time.Sleep(80 * time.Millisecond)
|
time.Sleep(800 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 3)
|
gtest.Assert(array.Len(), 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -193,6 +194,10 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
|||||||
gtest.Assert(array.Len(), 2)
|
gtest.Assert(array.Len(), 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Locker_RLockFunc2(t *testing.T) {
|
||||||
|
|
||||||
//Lock before RLockFuncs
|
//Lock before RLockFuncs
|
||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
key := "testLockBeforeRLockFuncs"
|
key := "testLockBeforeRLockFuncs"
|
||||||
@ -200,26 +205,29 @@ func Test_Locker_RLockFunc(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
gmlock.Lock(key)
|
gmlock.Lock(key)
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
time.Sleep(50 * time.Millisecond)
|
//glog.Println("add1")
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
gmlock.Unlock(key)
|
gmlock.Unlock(key)
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
gmlock.RLockFunc(key, func() {
|
gmlock.RLockFunc(key, func() {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
time.Sleep(70 * time.Millisecond)
|
//glog.Println("add2")
|
||||||
|
time.Sleep(700 * time.Millisecond)
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
gmlock.RLockFunc(key, func() {
|
gmlock.RLockFunc(key, func() {
|
||||||
array.Append(1)
|
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)
|
gtest.Assert(array.Len(), 1)
|
||||||
time.Sleep(70 * time.Millisecond)
|
time.Sleep(700 * time.Millisecond)
|
||||||
gtest.Assert(array.Len(), 3)
|
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.
|
// 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,
|
// If a copy of the MIT was not distributed with this file,
|
||||||
// You can obtain one at https://github.com/gogf/gf.
|
// 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 (
|
import (
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"github.com/gogf/gf/g/container/gtype"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The high level RWMutex.
|
// The high level Mutex, which implements more rich features for mutex.
|
||||||
// It wraps the sync.RWMutex to implements more rich features.
|
|
||||||
type Mutex struct {
|
type Mutex struct {
|
||||||
mu sync.RWMutex
|
state *gtype.Int32 // Indicates the state of mutex.
|
||||||
wid *gtype.Int64 // Unique id, used for multiple and safe logic Unlock.
|
writer *gtype.Int32 // Pending writer count.
|
||||||
locking *gtype.Bool // Locking mark for atomic operation for *Lock and Try*Lock functions.
|
reader *gtype.Int32 // Pending reader count.
|
||||||
// There must be only one locking operation at the same time for concurrent safe purpose.
|
writing chan struct{} // Channel used for writer blocking.
|
||||||
state *gtype.Int32 // Locking state:
|
reading chan struct{} // Channel used for reader blocking.
|
||||||
// 0: writing lock false;
|
|
||||||
// -1: writing lock true;
|
|
||||||
// >=1: reading lock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMutex creates and returns a new mutex.
|
// New creates and returns a new mutex.
|
||||||
func NewMutex() *Mutex {
|
func New() *Mutex {
|
||||||
return &Mutex{
|
return &Mutex{
|
||||||
wid: gtype.NewInt64(),
|
|
||||||
state: gtype.NewInt32(),
|
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,
|
// If the lock is already locked for reading or writing,
|
||||||
// Lock blocks until the lock is available.
|
// Lock blocks until the lock is available.
|
||||||
func (m *Mutex) Lock() {
|
func (m *Mutex) Lock() {
|
||||||
if m.locking.Cas(false, true) {
|
for {
|
||||||
m.mu.Lock()
|
// If there're no readers pending and writing lock currently,
|
||||||
// State should be changed after locks.
|
// then do the writing lock checks.
|
||||||
m.state.Set(-1)
|
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
|
||||||
m.wid.Add(1)
|
return
|
||||||
m.locking.Set(false)
|
}
|
||||||
} else {
|
m.writer.Add(1)
|
||||||
runtime.Gosched()
|
<-m.writing
|
||||||
m.Lock()
|
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.
|
// It is safe to be called multiple times if there's any locks or not.
|
||||||
func (m *Mutex) Unlock() {
|
func (m *Mutex) Unlock() {
|
||||||
if m.state.Cas(-1, 0) {
|
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 true if success, or if there's a write/reading lock on the mutex,
|
||||||
// it returns false.
|
// it returns false.
|
||||||
func (m *Mutex) TryLock() bool {
|
func (m *Mutex) TryLock() bool {
|
||||||
if m.locking.Cas(false, true) {
|
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
|
||||||
if m.state.Cas(0, -1) {
|
return true
|
||||||
m.mu.Lock()
|
|
||||||
m.wid.Add(1)
|
|
||||||
m.locking.Set(false)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
m.locking.Set(false)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -79,59 +81,106 @@ func (m *Mutex) TryLock() bool {
|
|||||||
// If the mutex is already locked for writing,
|
// If the mutex is already locked for writing,
|
||||||
// It blocks until the lock is available.
|
// It blocks until the lock is available.
|
||||||
func (m *Mutex) RLock() {
|
func (m *Mutex) RLock() {
|
||||||
if m.locking.Cas(false, true) {
|
var n int32
|
||||||
m.mu.RLock()
|
for {
|
||||||
// State should be changed after locks.
|
// If there're no writing lock and pending writers currently,
|
||||||
m.state.Add(1)
|
// then do the reading lock checks.
|
||||||
m.locking.Set(false)
|
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
|
||||||
} else {
|
if m.state.Cas(n, n+1) {
|
||||||
runtime.Gosched()
|
return
|
||||||
m.RLock()
|
} else {
|
||||||
|
runtime.Gosched()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Or else pending the reader.
|
||||||
|
m.reader.Add(1)
|
||||||
|
<-m.reading
|
||||||
|
m.reader.Add(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RUnlock unlocks the reading lock.
|
// RUnlock unlocks the reading lock.
|
||||||
// It is safe to be called multiple times if there's any locks or not.
|
// It is safe to be called multiple times if there's any locks or not.
|
||||||
func (m *Mutex) RUnlock() {
|
func (m *Mutex) RUnlock() {
|
||||||
if n := m.state.Val(); n >= 1 {
|
var n int32
|
||||||
if m.state.Cas(n, n-1) {
|
for {
|
||||||
m.mu.RUnlock()
|
if n = m.state.Val(); n >= 1 {
|
||||||
|
if m.state.Cas(n, n-1) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
} else {
|
} 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.
|
// 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.
|
// It returns true if success, or if there's a writing lock on the mutex, it returns false.
|
||||||
func (m *Mutex) TryRLock() bool {
|
func (m *Mutex) TryRLock() bool {
|
||||||
if m.locking.Cas(false, true) {
|
var n int32
|
||||||
if m.state.Val() >= 0 {
|
for {
|
||||||
m.mu.RLock()
|
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
|
||||||
m.state.Add(1)
|
if m.state.Cas(n, n+1) {
|
||||||
m.locking.Set(false)
|
return true
|
||||||
return true
|
} else {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
m.locking.Set(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 {
|
func (m *Mutex) IsLocked() bool {
|
||||||
return m.state.Val() != 0
|
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 {
|
func (m *Mutex) IsWLocked() bool {
|
||||||
return m.state.Val() < 0
|
return m.state.Val() < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRLocked checks whether the mutex is locked by reading lock.
|
// 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 {
|
func (m *Mutex) IsRLocked() bool {
|
||||||
return m.state.Val() > 0
|
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>.
|
// 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 true if success, or if there's a write/reading lock on the mutex,
|
||||||
// it returns false.
|
// it returns false.
|
||||||
@ -158,23 +207,3 @@ func (m *Mutex) TryRLockFunc(f func()) bool {
|
|||||||
}
|
}
|
||||||
return false
|
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
|
package grpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/glist"
|
"github.com/gogf/gf/g/container/glist"
|
||||||
"github.com/gogf/gf/g/container/gtype"
|
"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.
|
// Add pushes a new job to the pool using default goroutine pool.
|
||||||
// The job will be executed asynchronously.
|
// The job will be executed asynchronously.
|
||||||
func Add(f func()) {
|
func Add(f func()) error {
|
||||||
pool.Add(f)
|
return pool.Add(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns current goroutine count of default goroutine pool.
|
// Size returns current goroutine count of default goroutine pool.
|
||||||
func Size() int {
|
func Size() int {
|
||||||
return pool.count.Val()
|
return pool.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jobs returns current job count of default goroutine pool.
|
// Jobs returns current job count of default goroutine pool.
|
||||||
func Jobs() int {
|
func Jobs() int {
|
||||||
return pool.list.Len()
|
return pool.Jobs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add pushes a new job to the pool.
|
// Add pushes a new job to the pool.
|
||||||
// The job will be executed asynchronously.
|
// 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)
|
p.list.PushFront(f)
|
||||||
// check whether to create a new goroutine or not.
|
var n int
|
||||||
if p.count.Val() == p.limit {
|
for {
|
||||||
return
|
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()
|
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.
|
// 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.
|
// Close closes the goroutine pool, which makes all goroutines exit.
|
||||||
func (p *Pool) Close() {
|
func (p *Pool) Close() {
|
||||||
p.closed.Set(true)
|
p.closed.Set(true)
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
package gtest_test
|
package gtest_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/test/gtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCase(t *testing.T) {
|
func TestCase(t *testing.T) {
|
||||||
gtest.Case(t, func() {
|
gtest.Case(t, func() {
|
||||||
gtest.Assert(1, 1)
|
gtest.Assert(1, 1)
|
||||||
|
gtest.AssertNE(1, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
package gregex_test
|
package gregex_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gogf/gf/g/text/gregex"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/g/text/gregex"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pattern = `(\w+).+\-\-\s*(.+)`
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/third/github.com/theckman/go-flock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/os/gflock"
|
"github.com/gogf/gf/g/os/gflock"
|
||||||
"github.com/gogf/gf/g/os/glog"
|
"github.com/gogf/gf/g/os/glog"
|
||||||
"github.com/gogf/gf/g/os/gproc"
|
"github.com/gogf/gf/g/os/gproc"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
l := gflock.New("demo.lock")
|
l := gflock.New("demo.lock")
|
||||||
l.Lock()
|
l.Lock()
|
||||||
glog.Printf("locked by pid: %d", gproc.Pid())
|
glog.Printf("locked by pid: %d", gproc.Pid())
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
l.UnLock()
|
l.UnLock()
|
||||||
glog.Printf("unlocked by pid: %d", gproc.Pid())
|
glog.Printf("unlocked by pid: %d", gproc.Pid())
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/gogf/gf/g"
|
||||||
"time"
|
"github.com/gogf/gf/g/net/ghttp"
|
||||||
|
|
||||||
"github.com/gogf/gf/g/container/garray"
|
|
||||||
"github.com/gogf/gf/g/os/gmlock"
|
|
||||||
"github.com/gogf/gf/g/test/gtest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
type Order struct{}
|
||||||
mu := gmlock.NewMutex()
|
|
||||||
array := garray.New()
|
func (order *Order) Get(r *ghttp.Request) {
|
||||||
go func() {
|
r.Response.Write("GET")
|
||||||
mu.LockFunc(func() {
|
}
|
||||||
array.Append(1)
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
func main() {
|
||||||
fmt.Println("====unlock")
|
s := g.Server()
|
||||||
})
|
s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc{
|
||||||
}()
|
"BeforeServe": func(r *ghttp.Request) {
|
||||||
go func() {
|
r.Response.CORSDefault()
|
||||||
time.Sleep(50 * time.Millisecond)
|
},
|
||||||
fmt.Println("tryRLock1")
|
})
|
||||||
mu.TryRLockFunc(func() {
|
s.BindObjectRest("/api.v1/{.struct}", new(Order))
|
||||||
array.Append(1)
|
s.SetPort(8199)
|
||||||
fmt.Println("tryRLock1 success")
|
s.Run()
|
||||||
})
|
|
||||||
}()
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/gogf/gf/g"
|
||||||
"github.com/gogf/gf/g/os/gmlock"
|
"github.com/gogf/gf/g/net/ghttp"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
type Schedule struct{}
|
||||||
key := "test3"
|
|
||||||
gmlock.Lock(key, 200*time.Millisecond)
|
type Task struct{}
|
||||||
fmt.Println("TryLock:", gmlock.TryLock(key))
|
|
||||||
fmt.Println("TryLock:", gmlock.TryLock(key))
|
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