Skip to content

Commit

Permalink
first commit 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
mununki committed May 9, 2019
0 parents commit e1dc975
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 0 deletions.
Empty file added .gitignore
Empty file.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# GW (Go Watcher)

## Stacks

Built in Go

## Features

re-Run the command in case of any changes in project directory

## How to use

## Next to do
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/mattdamon108/gomon

go 1.12

require (
github.com/fsnotify/fsnotify v1.4.7
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c h1:hDn6jm7snBX2O7+EeTk6Q4WXJfKt7MWgtiCCRi1rBoY=
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
61 changes: 61 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
// "fmt"
"log"
"os"
"os/exec"

"github.com/fsnotify/fsnotify"
)

func runTest() *exec.Cmd {
p := exec.Command("./test-run")
p.Stdout = os.Stdout
p.Stderr = os.Stderr
p.Dir = "."
p.Start()

return p
}

func main() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()

p := runTest()

done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event: ", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file: ", event.Name)
}
p.Process.Kill()

p = runTest()
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error: ", err)
}
}
}()

err = watcher.Add(".")
if err != nil {
log.Fatal(err)
}

<-done
}
18 changes: 18 additions & 0 deletions runner/runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"log"
)

func main() {
log.Println("I'm running...")

done := make(chan bool)

go func() {
for {
}
}()

<-done
}
Binary file added test-run
Binary file not shown.

5 comments on commit e1dc975

@mcauto
Copy link

@mcauto mcauto commented on e1dc975 May 12, 2019

Choose a reason for hiding this comment

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

example_test.go

package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"
	"testing"

	"github.com/fsnotify/fsnotify"
)

func TestExampleCode(t *testing.T) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		t.Error(err)
	}
	defer watcher.Close()

	err = watcher.Add(".")
	if err != nil {
		t.Error(err)
	}

	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
MAIN:
	for {
		select {
		case event, ok := <-watcher.Events:
			if !ok { // 채널이 닫힌 경우 ex) close(watcher.Events)
				break MAIN
			}
			log.Println("event:", event)
			if event.Op&fsnotify.Write == fsnotify.Write {
				log.Println("modified file:", event.Name)
			}

		case err, ok := <-watcher.Errors:
			if !ok { // 채널이 닫힌 경우 ex) close(watcher.Errors)
				break MAIN
			}
			log.Println("error:", err)

		case signal := <-sigs:
			if signal == os.Interrupt {
				break MAIN
				// close(watcher.Events) 또는 close(watcher.Errors)
			}
		}
	}
}
$ go test -v example_test.go

@mununki
Copy link
Owner Author

@mununki mununki commented on e1dc975 May 12, 2019

Choose a reason for hiding this comment

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

이렇게하면 goroutine을 사용하지 않고도 구현이 가능하군요! 정말 감사합니다.
사실 for loop 상단에 레이블을 이용하는 방법은 몰랐었는데, 레퍼런스를 찾아보니, 이렇게 구현이 가능하겠네요. 그런데 궁금한게 하나 있습니다.

case signal := <-sigs:

이 블럭의 break MAIN은 CTRL-C를 눌렀을때 sigs 채널로 os.Interrupt가 수신되고 break MAIN으로 이동하여 for loop을 건너뛰게 되서 프로그램이 종료하게 되는거로 읽히는데요.

watcher.Events나 watcher.Errors를 수신하는 case의 경우 continue로 해야 계속 for loop이 돌면서 event를 기다리게 될 것으로 예상했는데, break MAIN 으로 해도 계속 for loop이 되는 이유가 뭔가요?

@mununki
Copy link
Owner Author

Choose a reason for hiding this comment

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

아! 제가 착각했군요. watcher.Events나 watcher.Errors를 수신하는 블럭에서 break MAIN이 되어야만 하겠네요. error 처리를 하는 경우니까요.

하나 여쭤보고 싶은게 있는데요. 기존처럼 goroutine안에 for-loop을 사용하고 done같은 chan을 하나 열어서 main 블럭 마지막에 <-done으로 goroutine을 돌린 체로 프로그램의 종료가 되지 않게 처리하는 방식과 이렇게 goroutine없이 for-loop만으로 하는 방식 중 어떤 것이 더 idiomatic 혹은 더 좋은 방식인가요?

@mcauto
Copy link

@mcauto mcauto commented on e1dc975 May 13, 2019

Choose a reason for hiding this comment

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

위의 예제 코드는 fsnotify의 설계적 문제로 인한 data race 이슈가 있네요. 하위 운영체제에서만 발생하는 이슈인 것 같다는 comment가 있지만 채널에 대한 동기화 처리 구조 문제 인 것 같습니다.
howeyc/fsnotify#7

context를 이용한 실패에 대한 회수 처리나
https://blog.golang.org/context
채널의 파이프라인 패턴 이 필요할 것 같네요
https://blog.golang.org/pipelines

goroutine과 channel을 이용한 동시성 처리 관련을 보고 선택하시면 좋을 것 같습니다.
https://blog.golang.org/pipelines
https://inconshreveable.com/07-08-2014/principles-of-designing-go-apis-with-channels/

@mununki
Copy link
Owner Author

Choose a reason for hiding this comment

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

참조 걸어주신 issue 정말 감사합니다. for+select만 사용한 경우 속도가 느린 시스템에서 data race 이슈가 발생할 수 있는 모양이네요. 모르던 사실이었는데 지적 감사합니다.

공유해주신 자료를 읽고 다시 goroutine을 사용해서 구현해봤는데, 제 코드에 확신이 잘 생기지 않네요.

https://github.com/mattdamon108/gw/blob/master/main.go

감사합니다.

Please sign in to comment.