github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/qct/downloader/api.go (about)

     1  package downloader
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	quickchain "github.com/quickchainproject/quickchain"
     8  	"github.com/quickchainproject/quickchain/event"
     9  	"github.com/quickchainproject/quickchain/rpc"
    10  )
    11  
    12  // PublicDownloaderAPI provides an API which gives information about the current synchronisation status.
    13  // It offers only methods that operates on data that can be available to anyone without security risks.
    14  type PublicDownloaderAPI struct {
    15  	d                         *Downloader
    16  	mux                       *event.TypeMux
    17  	installSyncSubscription   chan chan interface{}
    18  	uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest
    19  }
    20  
    21  // NewPublicDownloaderAPI create a new PublicDownloaderAPI. The API has an internal event loop that
    22  // listens for events from the downloader through the global event mux. In case it receives one of
    23  // these events it broadcasts it to all syncing subscriptions that are installed through the
    24  // installSyncSubscription channel.
    25  func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAPI {
    26  	api := &PublicDownloaderAPI{
    27  		d:   d,
    28  		mux: m,
    29  		installSyncSubscription:   make(chan chan interface{}),
    30  		uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest),
    31  	}
    32  
    33  	go api.eventLoop()
    34  
    35  	return api
    36  }
    37  
    38  // eventLoop runs a loop until the event mux closes. It will install and uninstall new
    39  // sync subscriptions and broadcasts sync status updates to the installed sync subscriptions.
    40  func (api *PublicDownloaderAPI) eventLoop() {
    41  	var (
    42  		sub               = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{})
    43  		syncSubscriptions = make(map[chan interface{}]struct{})
    44  	)
    45  
    46  	for {
    47  		select {
    48  		case i := <-api.installSyncSubscription:
    49  			syncSubscriptions[i] = struct{}{}
    50  		case u := <-api.uninstallSyncSubscription:
    51  			delete(syncSubscriptions, u.c)
    52  			close(u.uninstalled)
    53  		case event := <-sub.Chan():
    54  			if event == nil {
    55  				return
    56  			}
    57  
    58  			var notification interface{}
    59  			switch event.Data.(type) {
    60  			case StartEvent:
    61  				notification = &SyncingResult{
    62  					Syncing: true,
    63  					Status:  api.d.Progress(),
    64  				}
    65  			case DoneEvent, FailedEvent:
    66  				notification = false
    67  			}
    68  			// broadcast
    69  			for c := range syncSubscriptions {
    70  				c <- notification
    71  			}
    72  		}
    73  	}
    74  }
    75  
    76  // Syncing provides information when this nodes starts synchronising with the Quickchain network and when it's finished.
    77  func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) {
    78  	notifier, supported := rpc.NotifierFromContext(ctx)
    79  	if !supported {
    80  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
    81  	}
    82  
    83  	rpcSub := notifier.CreateSubscription()
    84  
    85  	go func() {
    86  		statuses := make(chan interface{})
    87  		sub := api.SubscribeSyncStatus(statuses)
    88  
    89  		for {
    90  			select {
    91  			case status := <-statuses:
    92  				notifier.Notify(rpcSub.ID, status)
    93  			case <-rpcSub.Err():
    94  				sub.Unsubscribe()
    95  				return
    96  			case <-notifier.Closed():
    97  				sub.Unsubscribe()
    98  				return
    99  			}
   100  		}
   101  	}()
   102  
   103  	return rpcSub, nil
   104  }
   105  
   106  // SyncingResult provides information about the current synchronisation status for this node.
   107  type SyncingResult struct {
   108  	Syncing bool                 `json:"syncing"`
   109  	Status  quickchain.SyncProgress `json:"status"`
   110  }
   111  
   112  // uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop.
   113  type uninstallSyncSubscriptionRequest struct {
   114  	c           chan interface{}
   115  	uninstalled chan interface{}
   116  }
   117  
   118  // SyncStatusSubscription represents a syncing subscription.
   119  type SyncStatusSubscription struct {
   120  	api       *PublicDownloaderAPI // register subscription in event loop of this api instance
   121  	c         chan interface{}     // channel where events are broadcasted to
   122  	unsubOnce sync.Once            // make sure unsubscribe logic is executed once
   123  }
   124  
   125  // Unsubscribe uninstalls the subscription from the DownloadAPI event loop.
   126  // The status channel that was passed to subscribeSyncStatus isn't used anymore
   127  // after this method returns.
   128  func (s *SyncStatusSubscription) Unsubscribe() {
   129  	s.unsubOnce.Do(func() {
   130  		req := uninstallSyncSubscriptionRequest{s.c, make(chan interface{})}
   131  		s.api.uninstallSyncSubscription <- &req
   132  
   133  		for {
   134  			select {
   135  			case <-s.c:
   136  				// drop new status events until uninstall confirmation
   137  				continue
   138  			case <-req.uninstalled:
   139  				return
   140  			}
   141  		}
   142  	})
   143  }
   144  
   145  // SubscribeSyncStatus creates a subscription that will broadcast new synchronisation updates.
   146  // The given channel must receive interface values, the result can either
   147  func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription {
   148  	api.installSyncSubscription <- status
   149  	return &SyncStatusSubscription{api: api, c: status}
   150  }