github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/neatptc/downloader/api.go (about) 1 package downloader 2 3 import ( 4 "context" 5 "sync" 6 7 "github.com/neatlab/neatio" 8 "github.com/neatlab/neatio/network/rpc" 9 "github.com/neatlab/neatio/utilities/event" 10 ) 11 12 type PublicDownloaderAPI struct { 13 d *Downloader 14 mux *event.TypeMux 15 installSyncSubscription chan chan interface{} 16 uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest 17 } 18 19 func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAPI { 20 api := &PublicDownloaderAPI{ 21 d: d, 22 mux: m, 23 installSyncSubscription: make(chan chan interface{}), 24 uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest), 25 } 26 27 go api.eventLoop() 28 29 return api 30 } 31 32 func (api *PublicDownloaderAPI) eventLoop() { 33 var ( 34 sub = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{}) 35 syncSubscriptions = make(map[chan interface{}]struct{}) 36 ) 37 38 for { 39 select { 40 case i := <-api.installSyncSubscription: 41 syncSubscriptions[i] = struct{}{} 42 case u := <-api.uninstallSyncSubscription: 43 delete(syncSubscriptions, u.c) 44 close(u.uninstalled) 45 case event := <-sub.Chan(): 46 if event == nil { 47 return 48 } 49 50 var notification interface{} 51 switch event.Data.(type) { 52 case StartEvent: 53 notification = &SyncingResult{ 54 Syncing: true, 55 Status: api.d.Progress(), 56 } 57 case DoneEvent, FailedEvent: 58 notification = false 59 } 60 61 for c := range syncSubscriptions { 62 c <- notification 63 } 64 } 65 } 66 } 67 68 func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) { 69 notifier, supported := rpc.NotifierFromContext(ctx) 70 if !supported { 71 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 72 } 73 74 rpcSub := notifier.CreateSubscription() 75 76 go func() { 77 statuses := make(chan interface{}) 78 sub := api.SubscribeSyncStatus(statuses) 79 80 for { 81 select { 82 case status := <-statuses: 83 notifier.Notify(rpcSub.ID, status) 84 case <-rpcSub.Err(): 85 sub.Unsubscribe() 86 return 87 case <-notifier.Closed(): 88 sub.Unsubscribe() 89 return 90 } 91 } 92 }() 93 94 return rpcSub, nil 95 } 96 97 type SyncingResult struct { 98 Syncing bool `json:"syncing"` 99 Status neatio.SyncProgress `json:"status"` 100 } 101 102 type uninstallSyncSubscriptionRequest struct { 103 c chan interface{} 104 uninstalled chan interface{} 105 } 106 107 type SyncStatusSubscription struct { 108 api *PublicDownloaderAPI 109 c chan interface{} 110 unsubOnce sync.Once 111 } 112 113 func (s *SyncStatusSubscription) Unsubscribe() { 114 s.unsubOnce.Do(func() { 115 req := uninstallSyncSubscriptionRequest{s.c, make(chan interface{})} 116 s.api.uninstallSyncSubscription <- &req 117 118 for { 119 select { 120 case <-s.c: 121 122 continue 123 case <-req.uninstalled: 124 return 125 } 126 } 127 }) 128 } 129 130 func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription { 131 api.installSyncSubscription <- status 132 return &SyncStatusSubscription{api: api, c: status} 133 }