vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.79k stars 2.16k forks source link

sync.waitgroup behaves differently from Golang #6870

Open axetroy opened 3 years ago

axetroy commented 3 years ago

V version: V 0.1.29 ed874ff OS: macos 11.0.1

What did you do?

I wrote a function to traverse directories using goroutine

// main.v
// run: v run main.v
module main

import os
import sync

fn walk(dir string, mut wg sync.WaitGroup, ch chan int) {
    defer {
        wg.done()
    }

    ch <- 1

    println('before: $ch.len')

    files := os.ls(dir) or {
        panic(err)
    }

    s := <-ch or {
        panic(err)
    }

    println('after: $ch.len')

    if s <0 {
        // do nothing
    }

    for file in files {
        target := os.join_path(dir, file)
        if os.is_dir(target) {
            wg.add(1)
            go walk(target, mut wg, ch)
        } else {
            // println(target)
            // ch <- 1
        }
    }
}

fn main() {
    mut wg := sync.new_waitgroup()

    wg.add(1)

    mut ch := chan int{cap: 255}

    go walk("/path/to/dir", mut wg, ch)

    wg.wait()
    ch.close()

    println('done')
}

What did you expect to see?

The proceeding should exit, wg.Wait() should execute like expect.

What did you see instead?

At least I didn't see anything wrong from the above code.

Normally, the process will exit normally.

But in reality, the process will hang. wg.wait() will never execute

For comparison, I wrote an equivalent Golang code

// main.go
// run: go run main.go
package main

import (
    "io/ioutil"
    "path"
    "sync"
)

func walk(dir string, wg *sync.WaitGroup, ch chan int) {
    defer wg.Done()

    ch <- 1

    println("before", len(ch))

    files, err := ioutil.ReadDir(dir)

    if err != nil {
        panic(err)
    }

    s := <-ch

    println("after", len(ch))

    if s < 0 {
        // do nothing
    }

    for _, file := range files {
        target := path.Join(dir, file.Name())
        if file.IsDir() {
            wg.Add(1)
            go walk(target, wg, ch)
        } else {
            // println(target)
            // ch <- 1
        }
    }
}

func main() {
    wg := sync.WaitGroup{}

    wg.Add(1)

    var ch = make(chan int, 255)

    go walk("/path/to/dir", &wg, ch)

    wg.Wait()
    close(ch)

    println("done")
}

and Golang's code works fine for me.

zakuro9715 commented 3 years ago

I tried it. walking './vlib' is works. But walking '/home/zakuro/src' is hungup. In golang, Works both.

I think, This problem occurs when target files too many.

axetroy commented 3 years ago

update:

V 0.2.2 c057b45, timestamp: 2021-02-15 18:56:26 +0200

still got this problme

danieldaeschle commented 3 years ago

@zakuro9715 for me it worked

axetroy commented 3 years ago

V 0.2.4 ccf6285

still got this with another error

V panic: `go main__walk()`: Resource temporarily unavailable
0   test                                0x000000010166a41d panic_error_number + 77
1   test                                0x00000001016574cf main__walk + 895
2   test                                0x000000010165712c main__walk_thread_wrapper + 44
3   libsystem_pthread.dylib             0x00007fff203d58fc _pthread_start + 224
4   libsystem_pthread.dylib             0x00007fff203d1443 thread_start + 15
JalonSolov commented 3 years ago

Interesting error.

I just tried your code on Manjaro Linux, with latest V 0.2.4 cd7d482 and it ran.

There were a few odd messages, but those are mostly because there are still 'panic's in vlib where there should be optionals.

I don't think your latest error is a V problem.

medvednikov commented 2 years ago

Works ok now.

axetroy commented 2 years ago

Works ok now.

@medvednikov Hi. I have retry it and it seems doesn't works for now

Current V version: V 0.3.1 436b19c, timestamp: 2022-09-04 14:15:26 +0300

$ v run test.v
...
...
...
after: 227
after: 224
before: 244
after: 231
before: 228
after: 227
before: 232
before: 255
after: 254
after: 253
before: 254
before: 255
after: 254
after: 253
V panic: `go main__walk()`: Resource temporarily unavailable
v hash: 436b19c
0   test                                0x000000010ddf83ed panic_error_number + 77
1   test                                0x000000010de2ea68 main__walk + 1192
2   test                                0x000000010dde32b1 main__walk_thread_wrapper + 49
3   test                                0x000000010de3d8ac GC_start_routine + 100
4   libsystem_pthread.dylib             0x00007ff805d9e4e1 _pthread_start + 125
5   libsystem_pthread.dylib             0x00007ff805d99f6b thread_start + 15
before: 254
after: 253
before: 254
after: 253
V panic: `go main__walk()`: Resource temporarily unavailable
v hash: 436b19c
0   test                                0x000000010ddf83ed panic_error_number + 77
1   test                                0x000000010de2ea68 main__walk + 1192
2   test                                0x000000010dde32b1 main__walk_thread_wrapper + 49