github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2013/advconc/fakemain/fakemain.go (about)

     1  // +build OMIT
     2  
     3  // fakemain runs the Subscribe example with a fake RSS fetcher.
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"time"
    10  )
    11  
    12  // STARTITEM OMIT
    13  // An Item is a stripped-down RSS item.
    14  type Item struct{ Title, Channel, GUID string }
    15  
    16  // STOPITEM OMIT
    17  
    18  // STARTFETCHER OMIT
    19  // A Fetcher fetches Items and returns the time when the next fetch should be
    20  // attempted.  On failure, Fetch returns a non-nil error.
    21  type Fetcher interface {
    22  	Fetch() (items []Item, next time.Time, err error)
    23  }
    24  
    25  // STOPFETCHER OMIT
    26  
    27  // STARTSUBSCRIPTION OMIT
    28  // A Subscription delivers Items over a channel.  Close cancels the
    29  // subscription, closes the Updates channel, and returns the last fetch error,
    30  // if any.
    31  type Subscription interface {
    32  	Updates() <-chan Item
    33  	Close() error
    34  }
    35  
    36  // STOPSUBSCRIPTION OMIT
    37  
    38  // STARTSUBSCRIBE OMIT
    39  // Subscribe returns a new Subscription that uses fetcher to fetch Items.
    40  func Subscribe(fetcher Fetcher) Subscription {
    41  	s := &sub{
    42  		fetcher: fetcher,
    43  		updates: make(chan Item),       // for Updates
    44  		closing: make(chan chan error), // for Close
    45  	}
    46  	go s.loop()
    47  	return s
    48  }
    49  
    50  // STOPSUBSCRIBE OMIT
    51  
    52  // sub implements the Subscription interface.
    53  type sub struct {
    54  	fetcher Fetcher         // fetches items
    55  	updates chan Item       // sends items to the user
    56  	closing chan chan error // for Close
    57  }
    58  
    59  // STARTUPDATES OMIT
    60  func (s *sub) Updates() <-chan Item {
    61  	return s.updates
    62  }
    63  
    64  // STOPUPDATES OMIT
    65  
    66  // STARTCLOSE OMIT
    67  // STARTCLOSESIG OMIT
    68  func (s *sub) Close() error {
    69  	// STOPCLOSESIG OMIT
    70  	errc := make(chan error)
    71  	s.closing <- errc // HLchan
    72  	return <-errc     // HLchan
    73  }
    74  
    75  // STOPCLOSE OMIT
    76  
    77  // loopCloseOnly is a version of loop that includes only the logic
    78  // that handles Close.
    79  func (s *sub) loopCloseOnly() {
    80  	// STARTCLOSEONLY OMIT
    81  	var err error // set when Fetch fails
    82  	for {
    83  		select {
    84  		case errc := <-s.closing: // HLchan
    85  			errc <- err      // HLchan
    86  			close(s.updates) // tells receiver we're done
    87  			return
    88  		}
    89  	}
    90  	// STOPCLOSEONLY OMIT
    91  }
    92  
    93  // loopFetchOnly is a version of loop that includes only the logic
    94  // that calls Fetch.
    95  func (s *sub) loopFetchOnly() {
    96  	// STARTFETCHONLY OMIT
    97  	var pending []Item // appended by fetch; consumed by send
    98  	var next time.Time // initially January 1, year 0
    99  	var err error
   100  	for {
   101  		var fetchDelay time.Duration // initally 0 (no delay)
   102  		if now := time.Now(); next.After(now) {
   103  			fetchDelay = next.Sub(now)
   104  		}
   105  		startFetch := time.After(fetchDelay)
   106  
   107  		select {
   108  		case <-startFetch:
   109  			var fetched []Item
   110  			fetched, next, err = s.fetcher.Fetch()
   111  			if err != nil {
   112  				next = time.Now().Add(10 * time.Second)
   113  				break
   114  			}
   115  			pending = append(pending, fetched...)
   116  		}
   117  	}
   118  	// STOPFETCHONLY OMIT
   119  }
   120  
   121  // loopSendOnly is a version of loop that includes only the logic for
   122  // sending items to s.updates.
   123  func (s *sub) loopSendOnly() {
   124  	// STARTSENDONLY OMIT
   125  	var pending []Item // appended by fetch; consumed by send
   126  	for {
   127  		var first Item
   128  		var updates chan Item // HLupdates
   129  		if len(pending) > 0 {
   130  			first = pending[0]
   131  			updates = s.updates // enable send case // HLupdates
   132  		}
   133  
   134  		select {
   135  		case updates <- first:
   136  			pending = pending[1:]
   137  		}
   138  	}
   139  	// STOPSENDONLY OMIT
   140  }
   141  
   142  // mergedLoop is a version of loop that combines loopCloseOnly,
   143  // loopFetchOnly, and loopSendOnly.
   144  func (s *sub) mergedLoop() {
   145  	// STARTFETCHVARS OMIT
   146  	var pending []Item
   147  	var next time.Time
   148  	var err error
   149  	// STOPFETCHVARS OMIT
   150  	for {
   151  		// STARTNOCAP OMIT
   152  		var fetchDelay time.Duration
   153  		if now := time.Now(); next.After(now) {
   154  			fetchDelay = next.Sub(now)
   155  		}
   156  		startFetch := time.After(fetchDelay)
   157  		// STOPNOCAP OMIT
   158  		var first Item
   159  		var updates chan Item
   160  		if len(pending) > 0 {
   161  			first = pending[0]
   162  			updates = s.updates // enable send case
   163  		}
   164  
   165  		// STARTSELECT OMIT
   166  		select {
   167  		case errc := <-s.closing: // HLcases
   168  			errc <- err
   169  			close(s.updates)
   170  			return
   171  			// STARTFETCHCASE OMIT
   172  		case <-startFetch: // HLcases
   173  			var fetched []Item
   174  			fetched, next, err = s.fetcher.Fetch() // HLfetch
   175  			if err != nil {
   176  				next = time.Now().Add(10 * time.Second)
   177  				break
   178  			}
   179  			pending = append(pending, fetched...) // HLfetch
   180  			// STOPFETCHCASE OMIT
   181  		case updates <- first: // HLcases
   182  			pending = pending[1:]
   183  		}
   184  		// STOPSELECT OMIT
   185  	}
   186  }
   187  
   188  // dedupeLoop extends mergedLoop with deduping of fetched items.
   189  func (s *sub) dedupeLoop() {
   190  	const maxPending = 10
   191  	// STARTSEEN OMIT
   192  	var pending []Item
   193  	var next time.Time
   194  	var err error
   195  	var seen = make(map[string]bool) // set of item.GUIDs // HLseen
   196  	// STOPSEEN OMIT
   197  	for {
   198  		// STARTCAP OMIT
   199  		var fetchDelay time.Duration
   200  		if now := time.Now(); next.After(now) {
   201  			fetchDelay = next.Sub(now)
   202  		}
   203  		var startFetch <-chan time.Time // HLcap
   204  		if len(pending) < maxPending {  // HLcap
   205  			startFetch = time.After(fetchDelay) // enable fetch case  // HLcap
   206  		} // HLcap
   207  		// STOPCAP OMIT
   208  		var first Item
   209  		var updates chan Item
   210  		if len(pending) > 0 {
   211  			first = pending[0]
   212  			updates = s.updates // enable send case
   213  		}
   214  		select {
   215  		case errc := <-s.closing:
   216  			errc <- err
   217  			close(s.updates)
   218  			return
   219  		// STARTDEDUPE OMIT
   220  		case <-startFetch:
   221  			var fetched []Item
   222  			fetched, next, err = s.fetcher.Fetch() // HLfetch
   223  			if err != nil {
   224  				next = time.Now().Add(10 * time.Second)
   225  				break
   226  			}
   227  			for _, item := range fetched {
   228  				if !seen[item.GUID] { // HLdupe
   229  					pending = append(pending, item) // HLdupe
   230  					seen[item.GUID] = true          // HLdupe
   231  				} // HLdupe
   232  			}
   233  			// STOPDEDUPE OMIT
   234  		case updates <- first:
   235  			pending = pending[1:]
   236  		}
   237  	}
   238  }
   239  
   240  // loop periodically fecthes Items, sends them on s.updates, and exits
   241  // when Close is called.  It extends dedupeLoop with logic to run
   242  // Fetch asynchronously.
   243  func (s *sub) loop() {
   244  	const maxPending = 10
   245  	type fetchResult struct {
   246  		fetched []Item
   247  		next    time.Time
   248  		err     error
   249  	}
   250  	// STARTFETCHDONE OMIT
   251  	var fetchDone chan fetchResult // if non-nil, Fetch is running // HL
   252  	// STOPFETCHDONE OMIT
   253  	var pending []Item
   254  	var next time.Time
   255  	var err error
   256  	var seen = make(map[string]bool)
   257  	for {
   258  		var fetchDelay time.Duration
   259  		if now := time.Now(); next.After(now) {
   260  			fetchDelay = next.Sub(now)
   261  		}
   262  		// STARTFETCHIF OMIT
   263  		var startFetch <-chan time.Time
   264  		if fetchDone == nil && len(pending) < maxPending { // HLfetch
   265  			startFetch = time.After(fetchDelay) // enable fetch case
   266  		}
   267  		// STOPFETCHIF OMIT
   268  		var first Item
   269  		var updates chan Item
   270  		if len(pending) > 0 {
   271  			first = pending[0]
   272  			updates = s.updates // enable send case
   273  		}
   274  		// STARTFETCHASYNC OMIT
   275  		select {
   276  		case <-startFetch: // HLfetch
   277  			fetchDone = make(chan fetchResult, 1) // HLfetch
   278  			go func() {
   279  				fetched, next, err := s.fetcher.Fetch()
   280  				fetchDone <- fetchResult{fetched, next, err}
   281  			}()
   282  		case result := <-fetchDone: // HLfetch
   283  			fetchDone = nil // HLfetch
   284  			// Use result.fetched, result.next, result.err
   285  			// STOPFETCHASYNC OMIT
   286  			fetched := result.fetched
   287  			next, err = result.next, result.err
   288  			if err != nil {
   289  				next = time.Now().Add(10 * time.Second)
   290  				break
   291  			}
   292  			for _, item := range fetched {
   293  				if id := item.GUID; !seen[id] { // HLdupe
   294  					pending = append(pending, item)
   295  					seen[id] = true // HLdupe
   296  				}
   297  			}
   298  		case errc := <-s.closing:
   299  			errc <- err
   300  			close(s.updates)
   301  			return
   302  		case updates <- first:
   303  			pending = pending[1:]
   304  		}
   305  	}
   306  }
   307  
   308  // naiveMerge is a version of Merge that doesn't quite work right.  In
   309  // particular, the goroutines it starts may block forever on m.updates
   310  // if the receiver stops receiving.
   311  type naiveMerge struct {
   312  	subs    []Subscription
   313  	updates chan Item
   314  }
   315  
   316  // STARTNAIVEMERGE OMIT
   317  func NaiveMerge(subs ...Subscription) Subscription {
   318  	m := &naiveMerge{
   319  		subs:    subs,
   320  		updates: make(chan Item),
   321  	}
   322  	// STARTNAIVEMERGELOOP OMIT
   323  	for _, sub := range subs {
   324  		go func(s Subscription) {
   325  			for it := range s.Updates() {
   326  				m.updates <- it // HL
   327  			}
   328  		}(sub)
   329  	}
   330  	// STOPNAIVEMERGELOOP OMIT
   331  	return m
   332  }
   333  
   334  // STOPNAIVEMERGE OMIT
   335  
   336  // STARTNAIVEMERGECLOSE OMIT
   337  func (m *naiveMerge) Close() (err error) {
   338  	for _, sub := range m.subs {
   339  		if e := sub.Close(); err == nil && e != nil {
   340  			err = e
   341  		}
   342  	}
   343  	close(m.updates) // HL
   344  	return
   345  }
   346  
   347  // STOPNAIVEMERGECLOSE OMIT
   348  
   349  func (m *naiveMerge) Updates() <-chan Item {
   350  	return m.updates
   351  }
   352  
   353  type merge struct {
   354  	subs    []Subscription
   355  	updates chan Item
   356  	quit    chan struct{}
   357  	errs    chan error
   358  }
   359  
   360  // STARTMERGESIG OMIT
   361  // Merge returns a Subscription that merges the item streams from subs.
   362  // Closing the merged subscription closes subs.
   363  func Merge(subs ...Subscription) Subscription {
   364  	// STOPMERGESIG OMIT
   365  	m := &merge{
   366  		subs:    subs,
   367  		updates: make(chan Item),
   368  		quit:    make(chan struct{}),
   369  		errs:    make(chan error),
   370  	}
   371  	// STARTMERGE OMIT
   372  	for _, sub := range subs {
   373  		go func(s Subscription) {
   374  			for {
   375  				var it Item
   376  				select {
   377  				case it = <-s.Updates():
   378  				case <-m.quit: // HL
   379  					m.errs <- s.Close() // HL
   380  					return              // HL
   381  				}
   382  				select {
   383  				case m.updates <- it:
   384  				case <-m.quit: // HL
   385  					m.errs <- s.Close() // HL
   386  					return              // HL
   387  				}
   388  			}
   389  		}(sub)
   390  	}
   391  	// STOPMERGE OMIT
   392  	return m
   393  }
   394  
   395  func (m *merge) Updates() <-chan Item {
   396  	return m.updates
   397  }
   398  
   399  // STARTMERGECLOSE OMIT
   400  func (m *merge) Close() (err error) {
   401  	close(m.quit) // HL
   402  	for _ = range m.subs {
   403  		if e := <-m.errs; e != nil { // HL
   404  			err = e
   405  		}
   406  	}
   407  	close(m.updates) // HL
   408  	return
   409  }
   410  
   411  // STOPMERGECLOSE OMIT
   412  
   413  // NaiveDedupe converts a stream of Items that may contain duplicates
   414  // into one that doesn't.
   415  func NaiveDedupe(in <-chan Item) <-chan Item {
   416  	out := make(chan Item)
   417  	go func() {
   418  		seen := make(map[string]bool)
   419  		for it := range in {
   420  			if !seen[it.GUID] {
   421  				// BUG: this send blocks if the
   422  				// receiver closes the Subscription
   423  				// and stops receiving.
   424  				out <- it // HL
   425  				seen[it.GUID] = true
   426  			}
   427  		}
   428  		close(out)
   429  	}()
   430  	return out
   431  }
   432  
   433  type deduper struct {
   434  	s       Subscription
   435  	updates chan Item
   436  	closing chan chan error
   437  }
   438  
   439  // Dedupe converts a Subscription that may send duplicate Items into
   440  // one that doesn't.
   441  func Dedupe(s Subscription) Subscription {
   442  	d := &deduper{
   443  		s:       s,
   444  		updates: make(chan Item),
   445  		closing: make(chan chan error),
   446  	}
   447  	go d.loop()
   448  	return d
   449  }
   450  
   451  func (d *deduper) loop() {
   452  	in := d.s.Updates() // enable receive
   453  	var pending Item
   454  	var out chan Item // disable send
   455  	seen := make(map[string]bool)
   456  	for {
   457  		select {
   458  		case it := <-in:
   459  			if !seen[it.GUID] {
   460  				pending = it
   461  				in = nil        // disable receive
   462  				out = d.updates // enable send
   463  				seen[it.GUID] = true
   464  			}
   465  		case out <- pending:
   466  			in = d.s.Updates() // enable receive
   467  			out = nil          // disable send
   468  		case errc := <-d.closing:
   469  			err := d.s.Close()
   470  			errc <- err
   471  			close(d.updates)
   472  			return
   473  		}
   474  	}
   475  }
   476  
   477  func (d *deduper) Close() error {
   478  	errc := make(chan error)
   479  	d.closing <- errc
   480  	return <-errc
   481  }
   482  
   483  func (d *deduper) Updates() <-chan Item {
   484  	return d.updates
   485  }
   486  
   487  // Fetch returns a Fetcher for Items from domain.
   488  func Fetch(domain string) Fetcher {
   489  	return fakeFetch(domain)
   490  }
   491  
   492  func fakeFetch(domain string) Fetcher {
   493  	return &fakeFetcher{channel: domain}
   494  }
   495  
   496  type fakeFetcher struct {
   497  	channel string
   498  	items   []Item
   499  }
   500  
   501  // FakeDuplicates causes the fake fetcher to return duplicate items.
   502  var FakeDuplicates bool
   503  
   504  func (f *fakeFetcher) Fetch() (items []Item, next time.Time, err error) {
   505  	now := time.Now()
   506  	next = now.Add(time.Duration(rand.Intn(5)) * 500 * time.Millisecond)
   507  	item := Item{
   508  		Channel: f.channel,
   509  		Title:   fmt.Sprintf("Item %d", len(f.items)),
   510  	}
   511  	item.GUID = item.Channel + "/" + item.Title
   512  	f.items = append(f.items, item)
   513  	if FakeDuplicates {
   514  		items = f.items
   515  	} else {
   516  		items = []Item{item}
   517  	}
   518  	return
   519  }
   520  
   521  func init() {
   522  	rand.Seed(time.Now().UnixNano())
   523  }
   524  
   525  // STARTMAIN OMIT
   526  func main() {
   527  	// STARTMERGECALL OMIT
   528  	// Subscribe to some feeds, and create a merged update stream.
   529  	merged := Merge(
   530  		Subscribe(Fetch("blog.golang.org")),
   531  		Subscribe(Fetch("googleblog.blogspot.com")),
   532  		Subscribe(Fetch("googledevelopers.blogspot.com")))
   533  	// STOPMERGECALL OMIT
   534  
   535  	// Close the subscriptions after some time.
   536  	time.AfterFunc(3*time.Second, func() {
   537  		fmt.Println("closed:", merged.Close())
   538  	})
   539  
   540  	// Print the stream.
   541  	for it := range merged.Updates() {
   542  		fmt.Println(it.Channel, it.Title)
   543  	}
   544  
   545  	panic("show me the stacks")
   546  }
   547  
   548  // STOPMAIN OMIT