Skip to content

Commit

Permalink
Merge pull request #2 from reugn/develop
Browse files Browse the repository at this point in the history
v0.3.0
  • Loading branch information
reugn authored Nov 21, 2021
2 parents 15e5b39 + 1a73892 commit 86d3c5b
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 87 deletions.
8 changes: 8 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
coverage:
status:
project:
default:
target: 80%
patch:
default:
target: 70%
21 changes: 21 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Build

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.17.x]
steps:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run coverage
run: go test . -coverprofile=coverage.out -covermode=atomic
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)
15 changes: 15 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: golangci-lint
on:
push:
branches:
- master
pull_request:

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea/
.vscode/
/vendor
coverage.out
40 changes: 40 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
run:
skip-dirs:
- generic

linters:
disable-all: true
enable:
- deadcode
- dupl
- errcheck
- exportloopref
- funlen
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- lll
- misspell
- prealloc
- revive
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck

issues:
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- errcheck
- unparam
16 changes: 0 additions & 16 deletions .travis.yml

This file was deleted.

5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<p align="center"><img src="./images/async.png" /></p>
<p align="center">Alternative sync library for Go.</p>
<p align="center">
<a href="https://travis-ci.org/reugn/async"><img src="https://travis-ci.org/reugn/async.svg?branch=master"></a>
<a href="https://godoc.org/github.com/reugn/async"><img src="https://godoc.org/github.com/reugn/async?status.svg"></a>
<a href="https://github.com/reugn/async/actions/workflows/build.yml"><img src="https://github.com/reugn/async/actions/workflows/build.yml/badge.svg"></a>
<a href="https://pkg.go.dev/github.com/reugn/async"><img src="https://pkg.go.dev/badge/github.com/reugn/async"></a>
<a href="https://goreportcard.com/report/github.com/reugn/async"><img src="https://goreportcard.com/badge/github.com/reugn/async"></a>
<a href="https://codecov.io/gh/reugn/async"><img src="https://codecov.io/gh/reugn/async/branch/master/graph/badge.svg"></a>
</p>
Expand All @@ -12,6 +12,7 @@
* **Promise** - While futures are defined as a type of read-only placeholder object created for a result which doesn’t yet exist, a promise can be thought of as a writable, single-assignment container, which completes a future.
* **Reentrant Lock** - Mutex that allows goroutines to enter into the lock on a resource more than once.
* **Optimistic Lock** - Mutex that allows optimistic reading. Could be retried or switched to RLock in case of failure. Significantly improves performance in case of frequent reads and short writes. See [benchmarks](./benchmarks/README.md).
* [**Go 1.18 Generics prototypes**](./generic)

## Examples
Can be found in the examples directory/tests.
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/lock_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/reugn/async"
)

//go test -bench=. -benchmem -v
// go test -bench=. -benchmem -v

type syncList struct {
list []string
Expand Down
76 changes: 41 additions & 35 deletions future.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,28 @@ import "sync"
// but will be available at some point, or an error if that value could not be made available.
type Future interface {

// Creates a new future by applying a function to the successful result of this future.
// Map creates a new Future by applying a function to the successful result of this Future.
Map(func(interface{}) (interface{}, error)) Future

// Creates a new future by applying a function to the successful result of
// this future, and returns the result of the function as the new future.
// FlatMap creates a new Future by applying a function to the successful result of
// this Future.
FlatMap(func(interface{}) (Future, error)) Future

// Blocks until Future completed and return either result or error.
// Get blocks until the Future is completed and returns either a result or an error.
Get() (interface{}, error)

// Creates a new future that will handle any error that this
// future might contain. If this future contains
// a valid result then the new future will contain the same.
// Recover handles any error that this Future might contain using a resolver function.
Recover(func() (interface{}, error)) Future

// Creates a new future that will handle any error that this
// future might contain by assigning it a value of another future.
// If this future contains a valid result then the new future will contain the same result.
// RecoverWith handles any error that this Future might contain using another Future.
RecoverWith(Future) Future

// Complete future with either result or error
// For Promise use internally
// complete completes the Future with either a value or an error.
// Is used by Promise internally.
complete(interface{}, error)
}

// FutureImpl Future implementation
// FutureImpl implements the Future interface.
type FutureImpl struct {
acc sync.Once
compl sync.Once
Expand All @@ -40,14 +36,14 @@ type FutureImpl struct {
err error
}

// NewFuture returns new Future
// NewFuture returns a new Future.
func NewFuture() Future {
return &FutureImpl{
done: make(chan interface{}),
}
}

// accept blocks once until result is available
// accept blocks once, until the Future result is available.
func (fut *FutureImpl) accept() {
fut.acc.Do(func() {
sig := <-fut.done
Expand All @@ -60,7 +56,8 @@ func (fut *FutureImpl) accept() {
})
}

// Map default implementation
// Map creates a new Future by applying a function to the successful result of this Future
// and returns the result of the function as a new Future.
func (fut *FutureImpl) Map(f func(interface{}) (interface{}, error)) Future {
next := NewFuture()
go func() {
Expand All @@ -74,7 +71,8 @@ func (fut *FutureImpl) Map(f func(interface{}) (interface{}, error)) Future {
return next
}

// FlatMap default implementation
// FlatMap creates a new Future by applying a function to the successful result of
// this Future and returns the result of the function as a new Future.
func (fut *FutureImpl) FlatMap(f func(interface{}) (Future, error)) Future {
next := NewFuture()
go func() {
Expand All @@ -93,35 +91,43 @@ func (fut *FutureImpl) FlatMap(f func(interface{}) (Future, error)) Future {
return next
}

// Get default implementation
// Get blocks until the Future is completed and returns either a result or an error.
func (fut *FutureImpl) Get() (interface{}, error) {
fut.accept()
return fut.value, fut.err
}

// Recover default implementation
// Recover handles any error that this Future might contain using a given resolver function.
// Returns the result as a new Future.
func (fut *FutureImpl) Recover(f func() (interface{}, error)) Future {
fut.accept()
if fut.err != nil {
next := NewFuture()
next.complete(f())
return next
}
return fut
next := NewFuture()
go func() {
fut.accept()
if fut.err != nil {
next.complete(f())
} else {
next.complete(fut.value, nil)
}
}()
return next
}

// RecoverWith default implementation
// RecoverWith handles any error that this Future might contain using another Future.
// Returns the result as a new Future.
func (fut *FutureImpl) RecoverWith(rf Future) Future {
fut.accept()
if fut.err != nil {
next := NewFuture()
next.complete(rf.Get())
return next
}
return fut
next := NewFuture()
go func() {
fut.accept()
if fut.err != nil {
next.complete(rf.Get())
} else {
next.complete(fut.value, nil)
}
}()
return next
}

// complete future with either value or error
// complete completes the Future with either a value or an error.
func (fut *FutureImpl) complete(v interface{}, e error) {
fut.compl.Do(func() {
go func() {
Expand Down
22 changes: 22 additions & 0 deletions generic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Go 1.18 Generics package
This package contains preliminary [`Go Generics`](https://github.com/golang/go/issues/43651) prototypes.

## Implemented data types
* asyncTask <sup>[1](#unexported)</sup>

<sup name="unexported">1</sup> Unexported since it is not possible to export generic code yet.

## Getting started

### Go 1.17
```sh
go test ./... -gcflags=-G=3 -vet=off
```

### Go 1.18+
Install [gotip](https://pkg.go.dev/golang.org/dl/gotip):
```sh
go install golang.org/dl/gotip@latest
gotip download
```
Use `gotip` to build and test the code.
31 changes: 31 additions & 0 deletions generic/task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// go:build go1.18

package generic

import (
"github.com/reugn/async"
)

// asyncTask is a data type for controlling possibly lazy & asynchronous computations.
type asyncTask[T any] struct {
taskFunc func() (T, error)
}

func newAsyncTask[T any](taskFunc func() (T, error)) *asyncTask[T] {
return &asyncTask[T]{
taskFunc: taskFunc,
}
}

func (task *asyncTask[T]) call() async.Future {
promise := async.NewPromise()
go func() {
res, err := task.taskFunc()
if err == nil {
promise.Success(res)
} else {
promise.Failure(err)
}
}()
return promise.Future()
}
21 changes: 21 additions & 0 deletions generic/task_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// go:build go1.18

package generic

import (
"testing"
"time"

"github.com/reugn/async/internal"
)

func TestAsyncTask(t *testing.T) {
task := newAsyncTask[string](func() (string, error) {
time.Sleep(1 * time.Second)
return "ok", nil
})
res, err := task.call().Get()

internal.AssertEqual(t, "ok", res.(string))
internal.AssertEqual(t, err, nil)
}
2 changes: 1 addition & 1 deletion goroutine.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"runtime/debug"
)

// GoroutineID returns current goroutine id
// GoroutineID returns the current goroutine id.
func GoroutineID() (uint, error) {
var id uint
var prefix string
Expand Down
Loading

0 comments on commit 86d3c5b

Please sign in to comment.