# Non-blocking network IO

**URL:** <https://discourse.julialang.org/t/non-blocking-network-io/123344>\
**Category:** General Usage\
**Created:** [December 2, 2024, 7:43am UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344 "2024-12-02T07:43:35Z")\
**Posts on this page:** 6\
**Page:** 2

<div class="post-metadata">

**Author:** ![phma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phma/32/6576_2.png) [@phma](https://discourse.julialang.org/u/phma)\
**Post date:** [December 5, 2024, 10:00am UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/22 "2024-12-05T10:00:43Z")

</div>

I think I may have figured it out:

```julia
julia> c1=accept(svr)
TCPSocket(RawFD(26) open, 0 bytes waiting)

julia> bytesavailable(c1)
0
# at this point I typed into netcat
julia> bytesavailable(c1)
0

julia> Base.start_reading(c1) # in stream.jl
0

julia> bytesavailable(c1)
5

julia> Base.start_reading(c1)
0

julia> read(c1,5)
5-element Vector{UInt8}:
 0x61
 0x6f
 0x65
 0x75
 0x0a
# typed some more
julia> bytesavailable(c1)
5

julia> read(c1,5)
5-element Vector{UInt8}:
 0x73
 0x6e
 0x74
 0x68
 0x0a

julia> bytesavailable(c1)
0
# typed some more
julia> bytesavailable(c1)
5

```

Does `start_reading` return immediately? There’s also `stop_reading`. I typed something, then tried `Base.start_reading(c1);Base.stop_reading(c1)`, but no bytes were available. Then I did `Base.start_reading(c1);sleep(0.001);Base.stop_reading(c1)` and got all seven bytes available.

---

<div class="post-metadata">

**Author:** ![phma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phma/32/6576_2.png) [@phma](https://discourse.julialang.org/u/phma)\
**Post date:** [December 5, 2024, 10:06am UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/23 "2024-12-05T10:06:26Z")

</div>

> [@Sukera](#):
>
> To my understanding this lock is shared across _all_ tasks, not just yours. So if there’s another library performing that long-winded call, it would theoretically block your code too.

You mean that if I run Julia and run this networking program in my Julia process, and someone else runs another Julia and sends a gigabyte, he could block my Julia?

In this Julia program, there won’t be another library transferring gigabytes at once.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [December 5, 2024, 10:15am UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/24 "2024-12-05T10:15:30Z")

</div>

> [@phma](#):
>
> You mean that if I run Julia and run this networking program in my Julia process, and someone else runs another Julia and sends a gigabyte, he could block my Julia?

No. I mean that if in your process, some other julia `Task` object sends (maybe even receives!) a large amount of data in one `read`/`write` call, it could result in your program having to wait for that operation to complete, due to that process-global shared lock.

I’m not taking about multiprocessing - that would not share ressources/locks between different julia instances anyway.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [December 6, 2024, 8:36am UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/25 "2024-12-06T08:36:05Z")

</div>

Ok, having run the experiment I mentioned above now, I have to rescind the claim that this `iolock_begin()` will cause other tasks to wait. I couldn’t observe this even with `julia -t 1` and having external `ncat` doing the processing/shoveling of data from `/dev/urandom`, with the julia `Task`s `read`/`write`ing the result. Maybe my methodology is wrong, or I misunderstood the code/purpose of that lock 🤔

---

<div class="post-metadata">

**Author:** ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)\
**Post date:** [December 18, 2024, 8:06pm UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/26 "2024-12-18T20:06:16Z")

</div>

> [@Sukera](#):
>
> Julia doesn’t currently have non-blocking I/O

As was true in 2017 from the link in OP’s post, and still remains true today, Julia uses non-blocking I/O for all streams, since Julia is green threaded (uses Tasks instead of threads):

> [@How to read from socket in non-blocking mode](https://discourse.julialang.org/t/how-to-read-from-socket-in-non-blocking-mode/4710/3):
>
> All I/O in julia is non-blocking in the traditional sense of the word, because we don’t block threads on I/O activity. However, we do use tasks to simulate blocking I/O for ease of programming. The nb\_available function isn’t super meaningful for sockets. It tells you how many bytes are available in the userspace buffer. It says nothing about now many bytes are available in the kernel, in various buffers along the network path or are in transit.

@phma the answers to all of your questions will be the same: use a task in some form (`@async` or `Threads.@spawn` are the most common) and write synchronous-looking code. It will automatically be converted into a non-blocking form internally (through the benefit of Tasks). When you are done with the socket (e.g. because it was told to shutdown) call `close` on all sockets that you want to destroy from any task.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [December 18, 2024, 9:41pm UTC](https://discourse.julialang.org/t/non-blocking-network-io/123344/27 "2024-12-18T21:41:50Z")

</div>

> [@jameson](#):
>
> As was true in 2017 from the link in OP’s post, and still remains true today, Julia uses non-blocking I/O for all streams, since Julia is green threaded (uses Tasks instead of threads):

I’d say that the exposed API is, by its nature, still blocking. The lowest form of parallel construct that Julia exposes is being blocked from progressing after all; asynchronous I/O would IMO imply that this is not the case, and that the function returns immediately, without having to spawn a `Task` by hand.

Sure, if you wrap every blocking call into a thunk that you then manually spawn a concurrent primitive for, you end up with a “non-blocking” API, but by that argument, using `pthread_spawn` around `write` in C also makes POSIX natively asynchronous (albeit horribly racy due to the lack of locks behind the scenes), even without `aio_read`/`aio_write`. This is of course nonsense - `write` is still a blocking call, otherwise there wouldn’t be a need for `aio_read`/`aio_write` or io\_uring in the first place.

[Previous page](https://discourse.julialang.org/t/non-blocking-network-io/123344.md?page=1)
