@@ -24,7 +24,6 @@ import (
24
24
"context"
25
25
"fmt"
26
26
"os"
27
- "sync"
28
27
29
28
bitfield "github.com/Stebalien/go-bitfield"
30
29
cid "github.com/ipfs/go-cid"
@@ -400,21 +399,16 @@ func (ds *Shard) getValue(ctx context.Context, hv *hashBits, key string, cb func
400
399
// EnumLinks collects all links in the Shard.
401
400
func (ds * Shard ) EnumLinks (ctx context.Context ) ([]* ipld.Link , error ) {
402
401
var links []* ipld.Link
403
- var setlk sync.Mutex
404
402
405
- getLinks := makeAsyncTrieGetLinks (ds .dserv , func (sv * Shard ) error {
406
- lnk := sv .val
407
- lnk .Name = sv .key
408
- setlk .Lock ()
409
- links = append (links , lnk )
410
- setlk .Unlock ()
411
- return nil
412
- })
413
-
414
- cset := cid .NewSet ()
403
+ linkResults := ds .EnumLinksAsync (ctx )
415
404
416
- err := dag .EnumerateChildrenAsync (ctx , getLinks , ds .nd .Cid (), cset .Visit )
417
- return links , err
405
+ for linkResult := range linkResults {
406
+ if linkResult .Err != nil {
407
+ return links , linkResult .Err
408
+ }
409
+ links = append (links , linkResult .Link )
410
+ }
411
+ return links , nil
418
412
}
419
413
420
414
// ForEachLink walks the Shard and calls the given function.
@@ -427,10 +421,28 @@ func (ds *Shard) ForEachLink(ctx context.Context, f func(*ipld.Link) error) erro
427
421
})
428
422
}
429
423
424
+ // EnumLinksAsync returns a channel which will receive Links in the directory
425
+ // as they are enumerated, where order is not gauranteed
426
+ func (ds * Shard ) EnumLinksAsync (ctx context.Context ) <- chan format.LinkResult {
427
+ linkResults := make (chan format.LinkResult )
428
+ ctx , cancel := context .WithCancel (ctx )
429
+ go func () {
430
+ defer close (linkResults )
431
+ defer cancel ()
432
+ getLinks := makeAsyncTrieGetLinks (ds .dserv , linkResults )
433
+ cset := cid .NewSet ()
434
+ err := dag .EnumerateChildrenAsync (ctx , getLinks , ds .nd .Cid (), cset .Visit )
435
+ if err != nil {
436
+ emitResult (ctx , linkResults , format.LinkResult {Link : nil , Err : err })
437
+ }
438
+ }()
439
+ return linkResults
440
+ }
441
+
430
442
// makeAsyncTrieGetLinks builds a getLinks function that can be used with EnumerateChildrenAsync
431
443
// to iterate a HAMT shard. It takes an IPLD Dag Service to fetch nodes, and a call back that will get called
432
444
// on all links to leaf nodes in a HAMT tree, so they can be collected for an EnumLinks operation
433
- func makeAsyncTrieGetLinks (dagService ipld.DAGService , onShardValue func ( shard * Shard ) error ) dag.GetLinks {
445
+ func makeAsyncTrieGetLinks (dagService ipld.DAGService , linkResults chan <- format. LinkResult ) dag.GetLinks {
434
446
435
447
return func (ctx context.Context , currentCid cid.Cid ) ([]* ipld.Link , error ) {
436
448
node , err := dagService .Get (ctx , currentCid )
@@ -458,16 +470,31 @@ func makeAsyncTrieGetLinks(dagService ipld.DAGService, onShardValue func(shard *
458
470
if err != nil {
459
471
return nil , err
460
472
}
461
- err = onShardValue (sv )
462
- if err != nil {
463
- return nil , err
464
- }
473
+ formattedLink := sv .val
474
+ formattedLink .Name = sv .key
475
+ emitResult (ctx , linkResults , format.LinkResult {Link : formattedLink , Err : nil })
465
476
}
466
477
}
467
478
return childShards , nil
468
479
}
469
480
}
470
481
482
+ func emitResult (ctx context.Context , linkResults chan <- format.LinkResult , r format.LinkResult ) {
483
+ // make sure that context cancel is processed first
484
+ // the reason is due to the concurrency of EnumerateChildrenAsync
485
+ // it's possible for EnumLinksAsync to complete and close the linkResults
486
+ // channel before this code runs
487
+ select {
488
+ case <- ctx .Done ():
489
+ return
490
+ default :
491
+ }
492
+ select {
493
+ case linkResults <- r :
494
+ case <- ctx .Done ():
495
+ }
496
+ }
497
+
471
498
func (ds * Shard ) walkTrie (ctx context.Context , cb func (* Shard ) error ) error {
472
499
for idx := range ds .children {
473
500
c , err := ds .getChild (ctx , idx )
0 commit comments