Spaces:
Configuration error
Configuration error
package concurrency | |
import ( | |
"context" | |
"sync" | |
) | |
// This is a Read-ONLY structure that contains the result of an arbitrary asynchronous action | |
type JobResult[RequestType any, ResultType any] struct { | |
request *RequestType | |
result *ResultType | |
err error | |
once sync.Once | |
done *chan struct{} | |
} | |
// This structure is returned in a pair with a JobResult and serves as the structure that has access to be updated. | |
type WritableJobResult[RequestType any, ResultType any] struct { | |
*JobResult[RequestType, ResultType] | |
} | |
// Wait blocks until the result is ready and then returns the result, or the context expires. | |
// Returns *ResultType instead of ResultType since its possible we have only an error and nil for ResultType. | |
// Is this correct and idiomatic? | |
func (jr *JobResult[RequestType, ResultType]) Wait(ctx context.Context) (*ResultType, error) { | |
if jr.done == nil { // If the channel is blanked out, result is ready. | |
return jr.result, jr.err | |
} | |
select { | |
case <-*jr.done: // Wait for the result to be ready | |
jr.done = nil | |
if jr.err != nil { | |
return nil, jr.err | |
} | |
return jr.result, nil | |
case <-ctx.Done(): | |
return nil, ctx.Err() | |
} | |
} | |
// Accessor function to allow holders of JobResults to access the associated request, without allowing the pointer to be updated. | |
func (jr *JobResult[RequestType, ResultType]) Request() *RequestType { | |
return jr.request | |
} | |
// This is the function that actually updates the Result and Error on the JobResult... but it's normally not accessible | |
func (jr *JobResult[RequestType, ResultType]) setResult(result ResultType, err error) { | |
jr.once.Do(func() { | |
jr.result = &result | |
jr.err = err | |
close(*jr.done) // Signal that the result is ready - since this is only ran once, jr.done cannot be set to nil yet. | |
}) | |
} | |
// Only the WritableJobResult can actually call setResult - prevents accidental corruption | |
func (wjr *WritableJobResult[RequestType, ResultType]) SetResult(result ResultType, err error) { | |
wjr.JobResult.setResult(result, err) | |
} | |
// NewJobResult binds a request to a matched pair of JobResult and WritableJobResult | |
func NewJobResult[RequestType any, ResultType any](request RequestType) (*JobResult[RequestType, ResultType], *WritableJobResult[RequestType, ResultType]) { | |
done := make(chan struct{}) | |
jr := &JobResult[RequestType, ResultType]{ | |
once: sync.Once{}, | |
request: &request, | |
done: &done, | |
} | |
return jr, &WritableJobResult[RequestType, ResultType]{JobResult: jr} | |
} | |