Skip to content

Commit

Permalink
v.util: make launch_tool more robust, when multiple v -b js file.v
Browse files Browse the repository at this point in the history
…commands are run at the same time (all trying to recompile the JS backend program) (#20631)
  • Loading branch information
spytheman committed Jan 22, 2024
1 parent b593f61 commit fe857f9
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .cirrus.yml
Expand Up @@ -2,7 +2,7 @@ env:
LANG: en_US.UTF-8

freebsd_instance:
image_family: freebsd-13-0
image_family: freebsd-13-2

## Note: all tasks should end with _script: here, otherwise they will not be picked up!
freebsd_task:
Expand Down
23 changes: 23 additions & 0 deletions vlib/os/filelock/lib.v
@@ -1,5 +1,7 @@
module filelock

import time

pub struct FileLock {
name string
mut:
Expand All @@ -12,3 +14,24 @@ pub fn new(fileName string) FileLock {
fd: -1
}
}

pub fn (mut l FileLock) wait_acquire(timeout time.Duration) bool {
fin := time.now().add(timeout)
for time.now() < fin {
if l.try_acquire() {
return true
}
time.sleep(1 * time.millisecond)
}
return false
}

pub fn (mut l FileLock) release() bool {
if l.fd != -1 {
unsafe {
l.unlink()
}
return true
}
return false
}
23 changes: 0 additions & 23 deletions vlib/os/filelock/lib_nix.c.v
@@ -1,7 +1,5 @@
module filelock

import time

#include <sys/file.h>

fn C.unlink(&char) int
Expand Down Expand Up @@ -32,27 +30,6 @@ pub fn (mut l FileLock) acquire() ! {
l.fd = fd
}

pub fn (mut l FileLock) release() bool {
if l.fd != -1 {
unsafe {
l.unlink()
}
return true
}
return false
}

pub fn (mut l FileLock) wait_acquire(s int) bool {
fin := time.now().add(s)
for time.now() < fin {
if l.try_acquire() {
return true
}
C.usleep(1000)
}
return false
}

fn open_lockfile(f string) int {
mut fd := C.open(&char(f.str), C.O_CREAT, 0o644)
if fd == -1 {
Expand Down
24 changes: 0 additions & 24 deletions vlib/os/filelock/lib_windows.c.v
@@ -1,7 +1,5 @@
module filelock

import time

fn C.DeleteFileW(&u16) bool
fn C.CreateFileW(&u16, u32, u32, voidptr, u32, u32, voidptr) voidptr
fn C.CloseHandle(voidptr) bool
Expand All @@ -26,28 +24,6 @@ pub fn (mut l FileLock) acquire() ! {
l.fd = fd
}

pub fn (mut l FileLock) release() bool {
if l.fd != -1 {
C.CloseHandle(l.fd)
l.fd = -1
t_wide := l.name.to_wide()
C.DeleteFileW(t_wide)
return true
}
return false
}

pub fn (mut l FileLock) wait_acquire(s int) bool {
fin := time.now().add(s)
for time.now() < fin {
if l.try_acquire() {
return true
}
time.sleep(1 * time.millisecond)
}
return false
}

fn open(f string) voidptr {
f_wide := f.to_wide()
// locking it
Expand Down
67 changes: 48 additions & 19 deletions vlib/v/util/util.v
Expand Up @@ -4,6 +4,8 @@
module util

import os
import os.filelock
import term
import rand
import time
import v.pref
Expand Down Expand Up @@ -177,27 +179,48 @@ pub fn launch_tool(is_verbose bool, tool_name string, args []string) {
}

current_work_dir := os.getwd()
// The goal of the random sleep intervals below, is to reduce the chance of a thundering herd, on `v test` with VJOBS>10,
// when each compiled _test.c.v file could also need a recompilation of the js_builder in cmd/tools/builders/ .
// TODO: use the os.filelock's API for inter process coordination instead.
time.sleep((5 + rand.intn(15) or { 0 }) * time.millisecond)
tool_recompile_retry_max_count := 7
for i in 0 .. tool_recompile_retry_max_count {
// ensure a stable and known working folder, when compiling V's tools, to avoid module lookup problems:
os.chdir(vroot) or {}
tool_compilation := os.execute(compilation_command)
os.chdir(current_work_dir) or {}
if tool_compilation.exit_code == 0 {
break
} else {
if i == tool_recompile_retry_max_count - 1 {
eprintln('cannot compile `${tool_source}`: ${tool_compilation.exit_code}\n${tool_compilation.output}')
exit(1)
tlog('recompiling ${tool_source}')
lockfile := tool_exe + '.lock'
tlog('lockfile: ${lockfile}')
mut l := filelock.new(lockfile)
if l.try_acquire() {
tlog('lockfile acquired')
tool_recompile_retry_max_count := 7
for i in 0 .. tool_recompile_retry_max_count {
tlog('looping i: ${i} / ${tool_recompile_retry_max_count}')
// ensure a stable and known working folder, when compiling V's tools, to avoid module lookup problems:
os.chdir(vroot) or {}
tool_compilation := os.execute(compilation_command)
os.chdir(current_work_dir) or {}
tlog('tool_compilation.exit_code: ${tool_compilation.exit_code}')
if tool_compilation.exit_code == 0 {
break
} else {
if i == tool_recompile_retry_max_count - 1 {
eprintln('cannot compile `${tool_source}`: ${tool_compilation.exit_code}\n${tool_compilation.output}')
l.release()
exit(1)
}
}
time.sleep((20 + rand.intn(40) or { 0 }) * time.millisecond)
}
tlog('lockfile releasing')
l.release()
tlog('lockfile released')
} else {
tlog('another process got the lock')
// wait till the other V tool recompilation process finished:
if l.wait_acquire(10 * time.second) {
tlog('the other process finished')
l.release()
} else {
tlog('timeout...')
}
time.sleep((20 + rand.intn(40) or { 0 }) * time.millisecond)
time.sleep((50 + rand.intn(40) or { 0 }) * time.millisecond)
tlog('result of the other process compiling ${tool_exe}: ${os.exists(tool_exe)}')
}
}
tlog('executing: ${tool_exe} with ${tool_args}')
$if windows {
cmd_system('${os.quoted_path(tool_exe)} ${tool_args}')
} $else $if js {
Expand All @@ -212,11 +235,17 @@ pub fn launch_tool(is_verbose bool, tool_name string, args []string) {
exit(2)
}

@[if trace_launch_tool ?]
fn tlog(s string) {
ts := time.now().format_ss_micro()
eprintln('${term.yellow(ts)} ${term.gray(s)}')
}

@[noreturn]
fn cmd_system(cmd string) {
res := os.system(cmd)
if res != 0 {
eprintln('> error ${res}, while executing: ${cmd}')
tlog('> error ${res}, while executing: ${cmd}')
}
exit(res)
}
Expand Down Expand Up @@ -485,7 +514,7 @@ pub fn prepare_tool_when_needed(source_name string) {
stool := os.join_path(vroot, 'cmd', 'tools', source_name)
tool_name, tool_exe := tool_source2name_and_exe(stool)
if should_recompile_tool(vexe, stool, tool_name, tool_exe) {
time.sleep(1001 * time.millisecond) // TODO: remove this when we can get mtime with a better resolution
time.sleep((1001 + rand.intn(20) or { 0 }) * time.millisecond) // TODO: remove this when we can get mtime with a better resolution
recompile_file(vexe, stool)
}
}
Expand Down

0 comments on commit fe857f9

Please sign in to comment.