DiceDB doesn't build on linux/arm64
Describe the bug
DiceDB doesn't build on linux/arm64. It gives the following error:
~:/dice# go build
# github.com/dicedb/dice/core
core/eval.go:178:44: undefined: syscall.SYS_FORK
As mentioned, it is because of the use of syscall.SYS_FORK in eval.go
func evalBGREWRITEAOF(args []string) []byte {
newChild, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
...
...
}
To Reproduce
Run go build on a linux/arm64 machine
Expected behavior The build should happen successfully without any error messages and generate a binary
Additional context Upon simply googling about this, came across this issue in golang repo: https://github.com/golang/go/issues/11981 Also, found a related stackoverflow answer: https://stackoverflow.com/questions/28370646/how-do-i-fork-a-go-process/28371586#28371586
TL;DR: Calling fork() using Syscall is unsafe, recommended API is ForkExec instead
@asutosh97 TL;DR:
⚠️⚠️ ⚠️⚠️
ForkExec() will lead to a broken AOF, don't use it here! It defeats the purpose of introducing Fork()
Here fork() is not used as in ForkExec() sense. Actually, the problem is not related to Fork() but it is POSIX. When I wrote this, it was clear that Fork() is not portable across platforms, on POSIX systems maybe. On the windows port of dice for example, I had implemented CoW pages using kernel32 API to mimic FORK call. I can write a FIX for arm64 if it is the target you want, that is very straightforward.
The issue with a lot of syscalls like fork, pipe, dup2, etc in arm64 is that arm64/Linux doesn't implement address space isolation perfectly. So arm64 implements some natives in GLIBC(userspace) to achieve that.
Regarding the safety of FORK, the ForkExec() is a combination of Fork() and Exec(), it destroys CoW and it is exactly the opposite of what Fsync() and other kernel32 things are supposed to do in conjunction with Fork(), the plain FORK is irreplaceable here. If @arpitbbhayani approves the target aarch64/arm64 Linux then I can issue the fix right away with arm64 natives to mimic an x86/fork otherwise we can close this issue.
Sometimes we write things for more performance and less safety. And sometimes we write for getting semantics right so that code won't blow up in kernel space. Golang is a system programming language, whether some operation is safe or unsafe is viewed in the context of the application. For a system language, it is quite common to use native or edgy stuff regularly, for instance, the windows implementation I wrote for Fsync and Fork is built over edgy stuff and that's why I'm still testing if my stuff breaks or not. Once safety guarantees are established, we can call it a Safe API, and that's how internally most Go system call interfaces are implemented.
Also references here - Why ForkExec() (Fork() and Exec()) is bad for CoW and page flushing(FSYNC)
https://unix.stackexchange.com/questions/469328/fork-and-cow-behavior-after-exec
Addendum:
- A port for arm64/Linux is not going to be easy since arm64 only provides
epoll_pwaitand notepoll_wait, fork is just a start, then epoll is waiting for a double whammy! - Primary goal is to make DiceDB compatible with Windows & System V ABI this includes Linux & macOS/Rosseta-based M1s also.
- On arm64 targets, it is only feasible to use a non-GC language to implement a DiceDB port. That could be addressed later along the course of development.
@arpitbbhayani should we go for an arm64 port?
I'll issue a fix for arm64 (m1 compatible) and the entire AOF stub will be upgraded to a stable implementation tomorrow.
Completed in #92