add future

This commit is contained in:
tursom 2023-04-30 13:15:10 +08:00
parent d3b71a7eeb
commit 6da2986c48
3 changed files with 241 additions and 0 deletions

132
concurrent/future/Future.go Executable file
View File

@ -0,0 +1,132 @@
package future
import (
"github.com/tursom/GoCollections/exceptions"
"github.com/tursom/GoCollections/lang"
"github.com/tursom/GoCollections/util/time"
)
type (
Future[T any] interface {
Get() (T, exceptions.Exception)
GetT(timeout time.Duration) (T, exceptions.Exception)
IsDone() bool
IsCancelled() bool
Cancel() bool
}
Task[T any] interface {
Future[T]
Done(value T)
Fail(e exceptions.Exception)
}
futureState uint8
impl[T any] struct {
state futureState
value T
e exceptions.Exception
done chan futureState
canceller func() bool
}
)
//goland:noinspection GoSnakeCaseUsage
const (
futureState_start futureState = iota
futureState_done futureState = iota
futureState_canceled futureState = iota
)
func Get[T any](future Future[T]) T {
get, e := future.Get()
if e != nil {
panic(e)
}
return get
}
func GetT[T any](future Future[T], timeout time.Duration) T {
get, e := future.GetT(timeout)
if e != nil {
panic(e)
}
return get
}
func New[T any](canceller func() bool) Task[T] {
return &impl[T]{
done: make(chan futureState, 1),
canceller: canceller,
}
}
func (i *impl[T]) Done(value T) {
if i.IsDone() {
return
}
i.value = value
i.state = futureState_done
i.done <- futureState_done
}
func (i *impl[T]) Fail(e exceptions.Exception) {
if i.IsDone() {
return
}
i.e = e
i.state = futureState_done
i.done <- futureState_done
}
func (i *impl[T]) Get() (T, exceptions.Exception) {
if i.IsDone() {
return i.value, i.e
}
<-i.done
return i.value, i.e
}
func (i *impl[T]) GetT(timeout time.Duration) (T, exceptions.Exception) {
if i.IsDone() {
return i.value, i.e
}
select {
case i.state = <-i.done:
case <-time.After(timeout):
return lang.Nil[T](), exceptions.NewTimeoutException(
"Future.GetT timeout", nil)
}
return i.value, i.e
}
func (i *impl[T]) IsDone() bool {
return i.state != futureState_start
}
func (i *impl[T]) IsCancelled() bool {
return i.state == futureState_canceled
}
func (i *impl[T]) Cancel() bool {
if i.IsDone() {
return false
}
if !i.canceller() {
return false
}
i.state = futureState_canceled
i.done <- futureState_canceled
return true
}

91
concurrent/future/mapper.go Executable file
View File

@ -0,0 +1,91 @@
package future
import (
"github.com/tursom/GoCollections/exceptions"
"github.com/tursom/GoCollections/lang"
"github.com/tursom/GoCollections/util/time"
)
type (
mapperFuture[V1, V2 any] struct {
future Future[V1]
mapper func(v1 V1) (V2, exceptions.Exception)
v V2
e exceptions.Exception
done bool
}
)
func Map[V1, V2 any](
future Future[V1],
mapper func(v1 V1) (V2, exceptions.Exception),
) Future[V2] {
return &mapperFuture[V1, V2]{
future: future,
mapper: mapper,
}
}
func (m *mapperFuture[V1, V2]) Get() (V2, exceptions.Exception) {
if m.done {
return m.v, m.e
}
defer func() {
m.done = true
}()
v1, e := m.future.Get()
if e != nil {
m.e = e
return lang.Nil[V2](), e
}
v2, e := m.mapper(v1)
if e != nil {
m.e = e
return lang.Nil[V2](), e
}
m.v = v2
return v2, nil
}
func (m *mapperFuture[V1, V2]) GetT(timeout time.Duration) (V2, exceptions.Exception) {
if m.done {
return m.v, m.e
}
v1, e := m.future.GetT(timeout)
if e != nil {
return m.processException(e)
}
v2, e := m.mapper(v1)
if e != nil {
return m.processException(e)
}
m.v = v2
return v2, nil
}
func (m *mapperFuture[V1, V2]) processException(e exceptions.Exception) (V2, exceptions.Exception) {
if _, ok := e.(*exceptions.TimeoutException); !ok {
m.done = true
m.e = e
}
return lang.Nil[V2](), e
}
func (m *mapperFuture[V1, V2]) IsDone() bool {
return m.future.IsDone()
}
func (m *mapperFuture[V1, V2]) IsCancelled() bool {
return m.future.IsCancelled()
}
func (m *mapperFuture[V1, V2]) Cancel() bool {
return m.future.Cancel()
}

18
exceptions/TimeoutException.go Executable file
View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2022 tursom. All rights reserved.
* Use of this source code is governed by a GPL-3
* license that can be found in the LICENSE file.
*/
package exceptions
type TimeoutException struct {
RuntimeException
}
func NewTimeoutException(message string, config *ExceptionConfig) *TimeoutException {
return &TimeoutException{
*NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.TimeoutException")),
}
}