commit e0bebbbb06a56611261dd1e165ce54f075b8659c Author: Sangbum Kim Date: Mon Apr 9 11:45:06 2018 +0900 added initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1565f94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ + +# Created by https://www.gitignore.io/api/intellij,go,linux,osx,windows + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea +*.iml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + +### Go ### +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + + +### Linux ### +*~ +*.swp + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### Windows ### +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/main.go b/main.go new file mode 100644 index 0000000..dc0794b --- /dev/null +++ b/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + mys "amuz.es/src/infra/goutils/sync" + "sync" + "time" + "math/rand" +) + +type philosopher struct { + eating bool + left *philosopher + right *philosopher + //table +} + +var current []int + +func anotherRoutine(cond *sync.Cond, j int) { + vb := time.Duration(rand.Intn(10)+1000) * time.Millisecond + time.Sleep(vb) + cond.L.Lock() + defer cond.L.Unlock() + cond.Wait() + current = append(current, j) +} +func monitor(cond *sync.Cond, i int, doneChan chan<- struct{}) { + defer close(doneChan) + j := 0 + for { + cond.L.Lock() + if j == len(current) { + cond.Signal() + } else { + j++ + //fmt.Printf("%d\n", current[j-1:j]) + fmt.Printf("%d\n", j) + //fmt.Println(current) + } + cond.L.Unlock() + if i == j { + return + } + } +} +func main() { + i := 10000 + doneChan := make(chan struct{}) + cond := sync.NewCond(&mys.ReentrantMutex{}) + go monitor(cond, i, doneChan) + + for j := 0; j < i; j++ { + go anotherRoutine(cond, j) + } + + <-doneChan + fmt.Printf("done %d\n", len(current)) +} diff --git a/runtime/reflect.go b/runtime/reflect.go new file mode 100644 index 0000000..6ef7fc6 --- /dev/null +++ b/runtime/reflect.go @@ -0,0 +1,43 @@ +package runtime + +import ( + "unsafe" + "reflect" + "fmt" +) + +//go:linkname typesByString reflect.typesByString +//go:nosplit +func typesByString(s string) []unsafe.Pointer + +/* +TypeByString tries to find a reflect.Type corresponding to the type specified by +s. +It calls the unexported `reflect.typesByString` to do so. It will fail if +the type can't be found or if more than one type with the given name exist. +This relies on the following assumptions: + * The signature of `reflect.typesByString` must not change + * The value returned by `reflect.TypeOf(0)` is a `*reflect.rtype` + * The `reflect.Value` struct contains a `ptr` field of type `unsafe.Pointer` +*/ +func TypeByString(s string) (reflect.Type, error) { + types := typesByString(s) + + if len(types) == 0 { + return nil, fmt.Errorf("Type '%s' not found", s) + } + if len(types) > 1 { + return nil, fmt.Errorf("Type '%s' is ambiguous", s) + } + + t := types[0] + + pRtypeType := reflect.ValueOf(reflect.TypeOf(0)).Type() + rtype := reflect.New(pRtypeType).Elem() + + ptr := unsafe.Pointer(reflect.ValueOf(rtype).FieldByName("ptr").Pointer()) + *(*unsafe.Pointer)(ptr) = t + + typ := rtype.Interface().(reflect.Type) + return typ, nil +} diff --git a/runtime/runtime.go b/runtime/runtime.go new file mode 100644 index 0000000..39824bc --- /dev/null +++ b/runtime/runtime.go @@ -0,0 +1,55 @@ +package runtime + +import ( + "reflect" + "unsafe" +) + +var ( + offsetGoid uintptr + offsetSched uintptr +) + +func init() { + pg, err := TypeByString("*runtime.g") + if err != nil { + return + } + if pg.Kind() != reflect.Ptr { + return + } + g := pg.Elem() + if g.Kind() != reflect.Struct { + return + } + goid, ok := g.FieldByName("goid") + if !ok { + return + } + sched, ok := g.FieldByName("sched") + if !ok { + return + } + offsetGoid = goid.Offset + offsetSched = sched.Offset +} + +//go:linkname mcall runtime.mcall +//go:nosplit +func mcall(func(uintptr)) + +//go:linkname gogo runtime.gogo +//go:nosplit +func gogo(uintptr) + + + +func GOID() (goid int64) { + mcall(func(g uintptr) { + id := unsafe.Pointer(g + offsetGoid) + goid = *(*int64)(id) + sched := g + offsetSched + gogo(sched) + }) + return +} diff --git a/runtime/stub.s b/runtime/stub.s new file mode 100644 index 0000000..34f84cf --- /dev/null +++ b/runtime/stub.s @@ -0,0 +1 @@ +// make compiler happy \ No newline at end of file diff --git a/sync/reentrant_lock.go b/sync/reentrant_lock.go new file mode 100644 index 0000000..d9c52bc --- /dev/null +++ b/sync/reentrant_lock.go @@ -0,0 +1,45 @@ +package sync + +import ( + "sync" + "amuz.es/src/infra/goutils/runtime" + "sync/atomic" + "fmt" +) + +type ReentrantMutex struct { + lock sync.Mutex + owner int64 // current lock owner + recursion int64 //current recursion level +} + +func (r *ReentrantMutex) Lock() { + caller := runtime.GOID() + // fast path + if atomic.LoadInt64(&r.owner) == caller { + r.recursion++ + return + } + + r.lock.Lock() + // we are now inside the lock + atomic.StoreInt64(&r.owner, caller) + r.recursion = 1 +} + +func (r *ReentrantMutex) Unlock() { + caller := runtime.GOID() + if atomic.LoadInt64(&r.owner) != caller { + panic(fmt.Sprintf("you are not the owner(%d): %d!", r.owner, caller)) + } + + r.recursion-- + // fast path + if r.recursion != 0 { + return + } + + // we are going to release the lock + atomic.StoreInt64(&r.owner, 0) + r.lock.Unlock() +}