Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solutions for problem 2 and 4 #41

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions 0-limit-crawler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ package main
import (
"fmt"
"sync"
"time"
)

var ticker <-chan time.Time

// Crawl uses `fetcher` from the `mockfetcher.go` file to imitate a
// real crawler. It crawls until the maximum depth has reached.
func Crawl(url string, depth int, wg *sync.WaitGroup) {
defer wg.Done()

if depth <= 0 {
return
}

<-ticker
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
Expand All @@ -37,12 +40,12 @@ func Crawl(url string, depth int, wg *sync.WaitGroup) {
// called concurrently
go Crawl(u, depth-1, wg)
}
return

Comment on lines -40 to +43
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks, didn't realize we had a redundant return here!

}

func main() {
var wg sync.WaitGroup

ticker = time.Tick(1 * time.Second)
wg.Add(1)
Crawl("http://golang.org/", 4, &wg)
wg.Wait()
Expand Down
31 changes: 31 additions & 0 deletions 1-producer-consumer/check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"strings"
"testing"
)

func TestMain(t *testing.T) {
result := processStream(GetMockStream())
lines := strings.Split(strings.TrimSpace(result), "\n")
expected := []string{
"davecheney \ttweets about golang",
"beertocode \tdoes not tweet about golang",
"ironzeb \ttweets about golang",
"beertocode \ttweets about golang",
"vampirewalk666 \ttweets about golang",
}
if len(lines) != len(expected)+1 { // +1 for the "Process took" line
t.Fatalf("Expected %d lines, got %d", len(expected)+1, len(lines))
}

for i, line := range expected {
if !strings.EqualFold(lines[i], line) {
t.Errorf("Line %d: expected %q, got %q", i+1, line, lines[i])
}
}

if !strings.HasPrefix(lines[len(lines)-1], "Process took") {
t.Errorf("Last line should start with 'Process took', got %q", lines[len(lines)-1])
}
}
40 changes: 28 additions & 12 deletions 1-producer-consumer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,56 @@
package main

import (
"bytes"
"fmt"
"sync"
"time"
)

func producer(stream Stream) (tweets []*Tweet) {
func producer(stream Stream, ch chan *Tweet) {
for {
tweet, err := stream.Next()
if err == ErrEOF {
return tweets
close(ch)
return
}

tweets = append(tweets, tweet)
ch <- tweet
}
}

func consumer(tweets []*Tweet) {
for _, t := range tweets {
func consumer(ch chan *Tweet, output *bytes.Buffer, wg *sync.WaitGroup) {
defer wg.Done()
for t := range ch {
if t.IsTalkingAboutGo() {
fmt.Println(t.Username, "\ttweets about golang")
fmt.Fprintln(output, t.Username, "\ttweets about golang")
} else {
fmt.Println(t.Username, "\tdoes not tweet about golang")
fmt.Fprintln(output, t.Username, "\tdoes not tweet about golang")
}
}
}

func main() {
func processStream(stream Stream) string {
start := time.Now()
stream := GetMockStream()
var output bytes.Buffer
var wg sync.WaitGroup

ch := make(chan *Tweet)

// Producer
tweets := producer(stream)
go producer(stream, ch)

// Consumer
consumer(tweets)
wg.Add(1)
go consumer(ch, &output, &wg)
wg.Wait()

fmt.Printf("Process took %s\n", time.Since(start))
fmt.Fprintf(&output, "Process took %s\n", time.Since(start))
return output.String()
}

func main() {
stream := GetMockStream()
result := processStream(stream)
fmt.Println(result)
}
5 changes: 5 additions & 0 deletions 2-race-in-cache/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package main

import (
"container/list"
"sync"
"testing"
)

Expand All @@ -32,6 +33,7 @@ type KeyStoreCache struct {
cache map[string]*list.Element
pages list.List
load func(string) string
mtx sync.Mutex
}

// New creates a new KeyStoreCache
Expand All @@ -44,6 +46,9 @@ func New(load KeyStoreCacheLoader) *KeyStoreCache {

// Get gets the key from cache, loads it from the source if needed
func (k *KeyStoreCache) Get(key string) string {
k.mtx.Lock()
defer k.mtx.Unlock()
// If key exists in cache, move the key to front of the doubly linked list
if e, ok := k.cache[key]; ok {
k.pages.MoveToFront(e)
return e.Value.(page).Value
Expand Down
27 changes: 26 additions & 1 deletion 4-graceful-sigint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,35 @@

package main

import (
"log"
"os"
"os/signal"
"time"
)

func main() {
// Create a process
proc := MockProcess{}

// Run the process (blocking)
proc.Run()
go proc.Run()

c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)

sig := <-c
log.Printf("captured sigint %v, stopping application and exiting...", sig)
go proc.Stop()

// Wait for either graceful stop or second SIGINT
select {
case <-time.After(10 * time.Second):
log.Println("Graceful shutdown completed.")
case sig := <-c:
log.Printf("Captured %v again, forcing shutdown...", sig)
}

os.Exit(0)

}