syscall write
returns -1 and set errno
is a trivial case. I am interested in the status of errno
if C write
call returning zero or positive. The wrapper syscall.Write
in Go simply returns err
if errno
is not zero for any case, which also includes the case of write
call returns positive.
However, the man page of C write
call roughly describes errno
may also be set but unspecified if we write zero length buffer without explaining any detail.
Thus, the following cases seem unclear:
errno
if write
call returning 0 for a file, a non-blocking socket, or a blocking socket?write
call returning 0 and errno
is not 0?errno
if write
call returning positive? Will it be negative?I think the above description points to the difference between C write
call and Go syscall.Write
, which is unclear for developers, here are my thoughts:
According to the man page, returning zero is clearly defined in C write
call for to files and for non-blocking sockets, but it's unclear whether there are non-error conditions for a blocking socket which would result in a write()
not blocking, returning 0, and (presumably) possibly succeeding later if retried.
Indeed Go directly wraps system call write
. However, the following code snippet seems not safe because written
equals to zero is a case that may trigger err
but we don't want to break the loop:
func writeAll(fd int, buffer []byte) bool {
length := len(buffer)
for length > 0 {
written, err := syscall.Write(fd, buffer)
if err != nil { // here
return false
}
length -= written
buffer = buffer[written:]
}
return true
}
Is there any wrong in my suspicion?
With write
, there are only two cases to consider:
errno
is set.errno
is not set.There are no other cases to consider, unless you are interested in historical Unix implementations (see: Is a return value of 0 from write(2) in C an error?).
The reason that write
may return 0 is because the input buffer may be empty.
However, the man page of C
write
call roughly describeserrno
may also be set but unspecified if we write zero length buffer without explaining any detail.
All this means is that it's possible for a 0-length write to fail. If it fails, it returns -1 and sets errno
. If it succeeds, it returns 0 and does not set errno
. This is the same behavior for any other write, it's just mentioned in the man page because people may find it surprising that a 0-length write could fail.
What is the status of
errno
ifwrite
call returning 0 for a file, a non-blocking socket, or a blocking socket?
In this case, errno
is not set, because write
did not fail. This will only happen if the input buffer is zero bytes.
When and how
write
call returning 0 anderrno
is not 0?
This does not happen. Either errno
is set and the return is -1, or errno
is not set and the return is 0 or larger.
What is the status of
errno
ifwrite
call returning positive? Will it be negative?
The errno
value will not be set. It will have the same value as it did before the write
call.
Is there any other syscall may encounter the same situation?
In general, system calls will either return an error or they will succeed. They won't do some mixture of both. Look at the Return Value section of other man pages and you will see that they are mostly the same as write
.
This code is safe.
func writeAll(fd int, buffer []byte) bool {
length := len(buffer)
for length > 0 {
written, err := syscall.Write(fd, buffer)
if err != nil { // here
return false
}
length -= written
buffer = buffer[written:]
}
return true
}
Note that it's a bit redundant, we can just do this:
func writeAll(fd int, buf []byte) bool {
for len(buf) > 0 {
n, err := syscall.Write(fd, buf)
if err != nil {
return false
}
buf = buf[n:]
}
return true
}
Technically, write
is both a system call and a C function (at least on many systems). However, the C function is just a stub which invokes the system call. Go does not call this stub, it invokes the system call directly, which means that C is not involved here (well, not until you get into the kernel).
The man page shows the calling conventions and behavior of the C stub, write
. Go chooses to copy that behavior in its own stub, syscall.Write
. The actual system call itself only has an assembly language interface.