Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 10
Good morning! Here's your coding interview problem for today.

This problem was asked by Apple.

Implement a job scheduler which takes in a function f and an integer n, and calls f after n milliseconds.

Pull request description:
Day10
Resolves #21 

Patch:
diff --git a/README.md b/README.md
index 6f4b50b..ccf6df4 100644
--- a/README.md
+++ b/README.md
@@ -19,3 +19,4 @@ problems from
 * [Day 7](https://github.com/vaskoz/dailycodingproblem-go/issues/15)
 * [Day 8](https://github.com/vaskoz/dailycodingproblem-go/issues/17)
 * [Day 9](https://github.com/vaskoz/dailycodingproblem-go/issues/19)
+* [Day 10](https://github.com/vaskoz/dailycodingproblem-go/issues/21)
diff --git a/day10/problem.go b/day10/problem.go
new file mode 100644
index 0000000..f9381da
--- /dev/null
+++ b/day10/problem.go
@@ -0,0 +1,60 @@
+package day10
+
+import (
+	"fmt"
+	"log"
+	"sync"
+	"time"
+)
+
+// Scheduler represents a job scheduler.
+type Scheduler struct {
+	queue     chan job
+	wg        sync.WaitGroup
+	startTime time.Time
+}
+
+type job struct {
+	f            func()
+	n            uint
+	jobStartTime time.Time
+}
+
+// NewScheduler returns a new instance of a running job scheduler.
+func NewScheduler() *Scheduler {
+	s := &Scheduler{queue: make(chan job), startTime: time.Now()}
+	s.wg.Add(1)
+	go func() {
+		for j := range s.queue {
+			s.wg.Add(1)
+			go func(j job) {
+				ms, _ := time.ParseDuration(fmt.Sprintf("%dms", j.n))
+				time.Sleep(ms)
+				log.Printf("starting job at %dms", convertNanoToMillis(time.Since(s.startTime).Nanoseconds()))
+				j.f()
+				log.Printf("completed job at %dms", convertNanoToMillis(time.Since(s.startTime).Nanoseconds()))
+				s.wg.Done()
+			}(j)
+		}
+	}()
+	return s
+}
+
+// Schedule schedules a new job to be run.
+func (s *Scheduler) Schedule(f func(), millis uint) {
+	current := convertNanoToMillis(time.Since(s.startTime).Nanoseconds())
+	log.Printf("Received at %dms scheduled for %dms", current, current+int64(millis))
+	s.queue <- job{f, millis, time.Now()}
+}
+
+// Shutdown prevents new jobs from being scheduled and allows the scheduler to terminate.
+func (s *Scheduler) Shutdown() {
+	close(s.queue)
+	s.wg.Done()
+	s.wg.Wait()
+	log.Printf("Scheduler shutdown complete at %dms", convertNanoToMillis(time.Since(s.startTime).Nanoseconds()))
+}
+
+func convertNanoToMillis(duration int64) int64 {
+	return duration / int64(1000000)
+}


Test patch:
diff --git a/day10/problem_test.go b/day10/problem_test.go
new file mode 100644
index 0000000..abebfad
--- /dev/null
+++ b/day10/problem_test.go
@@ -0,0 +1,30 @@
+package day10
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+func TestScheduler(t *testing.T) {
+	t.Parallel()
+	sched := NewScheduler()
+	sched.Schedule(func() { fmt.Println("hi there") }, 10)
+	sched.Schedule(func() { fmt.Println("hello there") }, 20)
+	sched.Schedule(func() { fmt.Println("so long") }, 30)
+	sched.Schedule(func() { time.Sleep(1 * time.Second); fmt.Println("i fell asleep") }, 1)
+	sched.Shutdown()
+}
+
+func TestSchedulerParallel(t *testing.T) {
+	t.Parallel()
+	sched := NewScheduler()
+	sched.Schedule(func() { fmt.Println("hi there") }, 10)
+	sched.Schedule(func() { fmt.Println("hello there") }, 20)
+	sched.Schedule(func() { fmt.Println("so long") }, 30)
+	sched.Schedule(func() { time.Sleep(2 * time.Second); fmt.Println("i fell asleep again") }, 1)
+	time.Sleep(2 * time.Second)
+	sched.Schedule(func() { fmt.Println("later jobs") }, 1)
+	sched.Schedule(func() { fmt.Println("later still") }, 10)
+	sched.Shutdown()
+}
diff --git a/day7/problem_test.go b/day7/problem_test.go
index 472f85d..076ab91 100644
--- a/day7/problem_test.go
+++ b/day7/problem_test.go
@@ -13,6 +13,7 @@ var testcases = []struct {
 }
 
 func TestNumberOfDecodings(t *testing.T) {
+	t.Parallel()
 	for _, tc := range testcases {
 		if result := NumberOfDecodings(tc.input); result != tc.expected {
 			t.Errorf("For input %v, expected %v but got %v", tc.input, tc.expected, result)
diff --git a/day8/problem_test.go b/day8/problem_test.go
index 2974769..80376a0 100644
--- a/day8/problem_test.go
+++ b/day8/problem_test.go
@@ -3,6 +3,7 @@ package day8
 import "testing"
 
 func TestCountUnivalSubtrees(t *testing.T) {
+	t.Parallel()
 	root := &BinaryTree{value: 0,
 		left: &BinaryTree{value: 1},
 		right: &BinaryTree{value: 0,
diff --git a/day9/problem_test.go b/day9/problem_test.go
index 0c7cb21..6e15f25 100644
--- a/day9/problem_test.go
+++ b/day9/problem_test.go
@@ -12,6 +12,7 @@ var testcases = []struct {
 }
 
 func TestMaximumNonAdjacentSum(t *testing.T) {
+	t.Parallel()
 	for _, tc := range testcases {
 		if result := MaximumNonAdjacentSum(tc.input); result != tc.expected {
 			t.Errorf("Expected %v, but got %v", tc.expected, result)