future

package module
v0.0.0-...-8d49790 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 16, 2016 License: GPL-3.0 Imports: 2 Imported by: 0

README

package future

Package future provides for asynchronous data access per the future pattern. It is a minimal treatment over the existing Go channels that allows the user to opt for partial or full use of the provided features.

background and motivation

Go is a very interesting language for various reasons. One very interesting aspect of Go, in my opinion, is that it resists misguided complexity. It is also an excellent bottom-up design language, in the sense that it is quite natural to code in the small and then incrementally add features as necessary.

I wrote the first version of this, when Go came out a few years ago, in Go-Redis asynchronous pipelines, and the intent was to extract it as a distinct package. Naturally it was basically a rendition of Java's Futures and how they were used in JRedis (the Java version of Go-Redis). And then for various reasons stopped writing more Go code.

Fast forward a few years to 2012. I started coding in Go again and in course of relearning the language started with the previously planned future extraction. The main motivation at this point is to (learn to) express design using idiomatic Go, and towards that learning goal this minor exercise has already been helpful, personally.

fine we're happy for you, but is it of any use to the rest of us?

Possibly.

The /comparative directory of the project contains two (effectively) identical programs. Both in terms of LOC, and performance, it is quite close. I personally find the future based code more clear in terms of intent, but that is a subjective view. And while a bit of selector code pretty much gives you basic 'futures' in Go without much ado, I remain firmly a believer in the utility of functions and libraries in building solid systems, as not repeating the same bit of code all over the code base lowers the probability of bugs. Unless performance is a primary consideration, I will likely use this package myself.

Also, if you are learning Go, you may find the changes between the original and final versions of interest.

bushwick, nyc April 2012

Documentation

Overview

The future package define the semantics of asynchronous (future value) data access.

The API can be used as either generic or type-safe manner: the utility of the former case is limited to wrapping a well-known channel/timer select pattern in a standard, tested, and modularized manner; and the latter obviates the need for casts but they do remain runtime mechanisms due to lack of support for generics.

The package may provide type-safe variants for builtin types. Users can (and are free) to write their own variants for custom types, if that is a requirement. A reference implementation supporting untyped futures is provided, and can be used via exported future.NewUntypedFuture.

API design was strongly inspired by Java's Futures.

-

Spec-Lite and Usage:

Futures are designed for asynchronous hand-off of a reference between 1 or 2 (typically 2) 'threads' of execution. Conceptually, it is nothing more than wrapping a standard pattern of using a channel (of size 1) to transfer a reference or a value. API is designed for minimal overhead and delegates any required type-safety to the programmer user.

The hand-off semantics are defined by the 'Future', and 'Provider' interfaces, where it is expected that the instantiator of future objects is the entity that has exposed an API returning future.Future(s).

The general pattern of use is as follows:

The implementation of a function | interface-method returning future.Future will instantiate a type supporting the future.Future & future.Provider interfaces. For example:

func AsyncSomething(...) (future.Future, ...)

This function | interface-method has the role of the future.Provider. For example:

// here using the future package's reference implementation
func RemoteServiceFoo (...) (response future.Future, ...) {
     response = future.NewUntypedFuture()

     // async func invoke the service call
     // using the future.Provider interface to fulfill the
     // future contract
     go func(future future.Provider) {
         // call the remote service
         svcresp, e := invokeRemoteService(...);
         switch {
         case e != nil:
             future.SetError(e)
         default:
             future.SetValue(svcresp)
         }
     }(response)
}

The consumer of the function | interface-method returning future.Future objects can (a) fire-and-forgetall-but-error, (b) block until results are provided, or (c) wait for a specified time (for cases such as meeting SLAs):

// ... call site to an async function | interface-method
futureResponse, ... := RemoteServiceFoo(...)

----------------------------

// idiom (a)
// fire and forgetall but the error
go func(future future.Future, errout io.Writer ) {
    result := future.Get()
    if result.IsError() {
        e := result.Error()
        errout.Write(e.Error())
    }
}(futureResponse, os.Stderr)

----------------------------

// idiom (b)
// block until future results provided
result := futureResponse.Get()
if result.IsError() {
    /* handle error */
} else {
    /* process response */
    response := result.Value().(ExpectedResponseTypeHere)
    ...
}

----------------------------

// idiom (c)
// wait for a given duration (per your SLA)
result, timeout := futureResponse.TryGet(slaMaxLatencyDuration)
if timeout {
    /* handle failture to meet SLA case */
    ...
    return
}
// congrats. Got the reponse. so now just process it per (b)
if result.IsError() {
    /* handle error */
} else {
    /* process response */
    response := result.Value().(ExpectedResponseTypeHere)
    ...
}

The behavior of future.Future is _only specified_ given the constraint that the receiving party adheres to the following:

a) repeated subsequent calls to FutureResult#TryGet can be made if the
   calls result in timeouts.

b) future.Future#Get must only be called once.  It may be called in isolation
   or can be called after one or more calls to TryGet, per timeout spec of
   `a` above).  Any other pattern of use is unspecified and is considered
   a programmer error.

The Result interface reference obtained by the receiver per above is conceptually a 'union' between an 'error' or 'value' (both regardless typed as interface{}).

This package makes no assumptions about the ownership and life-cycle of the references (whether error or value) handed off via the futures, or for that matter, the references to Future objects themselves. Typically, the life-cycle of all objects created are close to the event bounds of creating the Future and obtaining either an error or value result, but that is entirely up to the user of the package. The untyped implementation does not maintain any references to the objects it creates.

Compliant implentations are required to release any rosources associated with a future.Future object on a successful future.Future#Get | future.Future#TryGet.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewUntypedFuture

func NewUntypedFuture() *futureResult

Creates a new untyped Future object.

Types

type Future

type Future interface {
	// Blocking get waits until future.Result is available.
	Get() (r Result)

	// Returns result or timeouts after specified wait duration
	TryGet(wait time.Duration) (r Result, timeout bool)
}

future.Future defines the api of future objects. Objects supporting this interface are created by the async function/service and returned to call site.

type Provider

type Provider interface {

	// sets the value of the fchan Result
	// Future.Value will be nil
	// A non-nil error is returned if already set.
	SetError(e error) error

	// sets an erro fchan Result - note that nil values are NOT permitted.
	// Future.Error will be nil
	// A non-nil error is returned if already set.
	SetValue(v interface{}) error
}

future.Provider defines the api for use by the provider of future.Results

type Result

type Result interface {
	// Value result.
	// Value of the Result - Value may be nil ONLY in case of Errors.
	Value() interface{}

	// Error result.
	// if Error is NOT nil, then Value must be nil.
	Error() error

	// convenience method
	IsError() bool
}

future.Result defines the type-generic future value results returned via a future.Future.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL