# Allocations with write(::TCPSocket, x)

**URL:** <https://discourse.julialang.org/t/allocations-with-write-tcpsocket-x/68865>\
**Category:** Internals & Design\
**Created:** [September 28, 2021, 10:09am UTC](https://discourse.julialang.org/t/allocations-with-write-tcpsocket-x/68865 "2021-09-28T10:09:45Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![DatName](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datname/32/418_2.png) [@DatName](https://discourse.julialang.org/u/DatName)\
**Post date:** [September 28, 2021, 10:09am UTC](https://discourse.julialang.org/t/allocations-with-write-tcpsocket-x/68865/1 "2021-09-28T10:09:45Z")

</div>

Hi.  
I am investigating the possibility of using TCPSockets without any dynamic allocations.

It seems that plain

```julia
@allocated write(soc::TCPSocket, x)

```

always allocates (after compilation). `--track-allocation=all` points to `wait()::Cint` and `ccall(:jl_switch, Cvoid, ())` inside it.  
Digging a bit further I found that actual change in number of allocated bytes tracked by garbage collector (`jl_gc_get_total_bytes()`) happens inside `uv_run(loop, UV_RUN_ONCE)` in `jl_task_get_next()`. From there I tried to narrow down sources of allocations but without any success.

Now, on the other hand, if I use `uv_write()` directly like this:

```julia
function write_cb(req::Ptr{Cvoid}, status::Cint)::Nothing
    Libc.free(req)
    nothing
end

global const cb = @cfunction(write_cb, Cvoid, (Ptr{Cvoid}, Cint))

function write(s::TCPSocket, x::Vector{UInt8})
    global cb
    p = pointer(x)
    n = UInt64(length(x))

    # Base.check_open(s)

    while !iszero(n)
        uvw = Libc.malloc(Base._sizeof_uv_write)
        nwrite::UInt64 = min(n, Base.MAX_OS_WRITE) # split up the write into chunks the OS can handle.
        
        # TODO: use writev instead of a loop
        
        err = ccall(:jl_uv_write,
                    Int32,
                    (Ptr{Cvoid}, Ptr{Cvoid}, UInt, Ptr{Cvoid}, Ptr{Cvoid}),
                    s, p, nwrite, uvw,
                    cb)
        if err < 0
            Base.uv_error("write", err)
        end

        n -= nwrite
    end

    ccall(:jl_process_events, Cint, ())
    return
end

```

than I get zero allocations from @allocated and actual RAM usage does not grow over time when sending millions of small vectors.

But the problem with that is that I do not understand consequences of calling `jl_process_events` at the very end of `write()` and how it would interact with the rest of the julia runtime? Is it safe to do it at all?

And the big question for me still is: why `write(::TCPSocket, x)` dynamically allocates?

---

<div class="post-metadata">

**Author:** ![AMJ](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/amj/32/214096_2.png) [@AMJ](https://discourse.julialang.org/u/AMJ)\
**Post date:** [April 6, 2023, 2:56pm UTC](https://discourse.julialang.org/t/allocations-with-write-tcpsocket-x/68865/2 "2023-04-06T14:56:56Z")

</div>

Have you found using `jl_process_events` unsafe?

Or did you found any solutions regarding `write()` function?
