11package runner
22
33import (
4+ "fmt"
45 "sync"
56
67 "github.com/gopherjs/gopherjs.github.io/playground/internal/common"
@@ -16,68 +17,90 @@ const (
1617)
1718
1819type packageCache struct {
19- output common.Output
2020 fetcher common.Fetcher
2121 cached map [string ]* sources.Sources
2222 inprogress map [string ]chan struct {}
2323 lock sync.Mutex
2424}
2525
26- func newPackageCache (output common. Output , fetcher common.Fetcher ) * packageCache {
26+ func newPackageCache (fetcher common.Fetcher ) * packageCache {
2727 return & packageCache {
28- output : output ,
2928 fetcher : fetcher ,
3029 cached : make (map [string ]* sources.Sources ),
3130 inprogress : make (map [string ]chan struct {}),
3231 }
3332}
3433
35- func (pc * packageCache ) Load (importPath string ) (* sources.Sources , loadResult ) {
34+ func (pc * packageCache ) Load (importPath string ) (* sources.Sources , loadResult , error ) {
3635 srcs := & sources.Sources {}
37- result := pc .syncLoad (importPath )(srcs )
38- return srcs , result
36+ result , err := pc .syncLoad (importPath )(srcs )
37+ return srcs , result , err
3938}
4039
40+ type syncLoadFunc func (srcs * sources.Sources ) (loadResult , error )
41+
4142// syncLoad returns a function to complete loading the package.
4243//
4344// The returned function may either return the cached package immediately,
4445// wait for an in-progress load to complete, or perform the load itself.
4546// syncLoad will not block, but the returned function may block.
46- func (pc * packageCache ) syncLoad (importPath string ) func (srcs * sources.Sources ) loadResult {
47+ //
48+ // If the returned function is run and returns loadFailed, the error will be non-nil
49+ // and if the error is non-nil, the loadResult will be loadFailed.
50+ func (pc * packageCache ) syncLoad (importPath string ) syncLoadFunc {
4751 pc .lock .Lock ()
4852 defer pc .lock .Unlock ()
4953
5054 if cached , found := pc .cached [importPath ]; found {
51- return func (srcs * sources.Sources ) loadResult {
52- * srcs = * cached // Copy the cached sources.
53- return loadCached
54- }
55+ return pc .alreadyLoaded (cached )
5556 }
5657
5758 // Load is already in progress, wait for it to complete.
5859 if ch , loading := pc .inprogress [importPath ]; loading {
59- return func (srcs * sources.Sources ) loadResult {
60- <- ch // Wait for the in-progress load to complete.
61-
62- pc .lock .Lock ()
63- defer pc .lock .Unlock ()
64-
65- if cached , found := pc .cached [importPath ]; found {
66- * srcs = * cached // Copy the cached sources.
67- return loadCached
68- }
69- return loadFailed
70- }
60+ return pc .alreadyInprogress (importPath , ch )
7161 }
7262
7363 // Load is not in progress, start it now.
7464 ch := make (chan struct {})
7565 pc .inprogress [importPath ] = ch
76- return func (srcs * sources.Sources ) loadResult {
66+ return pc .startLoading (importPath , ch )
67+ }
68+
69+ // alreadyLoaded returns a syncLoadFunc that immediately returns the cached sources.
70+ // The given cached sources must be the sources that are already loaded in the cache.
71+ func (pc * packageCache ) alreadyLoaded (cached * sources.Sources ) syncLoadFunc {
72+ return func (srcs * sources.Sources ) (loadResult , error ) {
73+ * srcs = * cached // Copy the cached sources.
74+ return loadCached , nil
75+ }
76+ }
77+
78+ // alreadyInprogress returns a syncLoadFunc that waits for an in-progress load to complete.
79+ // The given channel will be waited on until it is closed, indicating the load is complete
80+ // and the sources should now be in the cache.
81+ func (pc * packageCache ) alreadyInprogress (importPath string , ch chan struct {}) syncLoadFunc {
82+ return func (srcs * sources.Sources ) (loadResult , error ) {
83+ <- ch // Wait for the in-progress load to complete.
84+
85+ pc .lock .Lock ()
86+ defer pc .lock .Unlock ()
87+
88+ if cached , found := pc .cached [importPath ]; found {
89+ * srcs = * cached // Copy the cached sources.
90+ return loadCached , nil
91+ }
92+ return loadFailed , fmt .Errorf (`failed to find package %q in cache after waiting load` , importPath )
93+ }
94+ }
95+
96+ // startLoading returns a syncLoadFunc that performs the package load.
97+ // The given channel will be closed when the load is complete to indicate to any
98+ // other processed waiting on the load that it is done.
99+ func (pc * packageCache ) startLoading (importPath string , ch chan struct {}) syncLoadFunc {
100+ return func (srcs * sources.Sources ) (loadResult , error ) {
77101 fetched , err := pc .fetcher .FetchPackage (importPath )
78102 if err != nil {
79- pc .output .AddError (err )
80- return loadFailed
103+ return loadFailed , err
81104 }
82105
83106 pc .lock .Lock ()
@@ -87,6 +110,6 @@ func (pc *packageCache) syncLoad(importPath string) func(srcs *sources.Sources)
87110 * srcs = * fetched // Copy the fetched sources.
88111 delete (pc .inprogress , importPath )
89112 close (ch )
90- return loadFetched
113+ return loadFetched , nil
91114 }
92115}
0 commit comments