Trying to better understand how Julia listens on a UNIX domain socket or named pipe

listen tells the OS you want to receive TCP/IP connectios on a port. accept waits for a connection to come in. While it is waiting other tasks get to run. So the reason to put the whole block in an @async element is to allow you to do something else while waiting for a connection. If you don’t want to do anything (including enter commands in REPL) then you don’t need the first @async.

The while true loop is just there so you can accept the next connection and the next and the next. In this way you can have multiple connections active at once. The inner @async is there so you can go back and wait for another connection. Normally there is a backlog of about 16 connections, (it may be more these days). That means that 16 additional connections can come in and the OS will hold them for you until you call accept again. The 17 connection will be rejected, so you need to get back and call accept as fast as you can. Also if you don’t accept soon enough the connection could time out and the remote end will give up. So the inner @async is to create a task to handle the connection so the main task can go back and accept the next connection.

A connection can drop at any time. Someone could pull a network cable, a computer can crash, a router can crash, a program can crash. The isopen() ensures that connection is open “now”. In this case it also appears to be so that the client could send multiple JSON objects before it closes the connection. So if the client sent one JSON object then closed the connection, you would read one JSON object the while would be false, and you are done. If the client sent two you would repeat the while twice then the connection would be closed and you would be done.

Reading and writing to the network, screen, or disk are all tasks that “take a long time” in computer terms. Basically you are waiting for hardware to do something and the CPU is idle. With Julia when you do these operations the current task gives up the CPU so another task can run.

So with this example the outer @async task gets to run when when there is a connection and the inner @async tasks are waiting. The inner @async tasks run when they actually need the CPU and nobody else is doing anything with it. Where this will all break down is if you have calculate something that takes a long time, like say pie to the millionth decimal point. At that point threads need to get involved, or you need to add “yields/sleeps” into the code so that the CPU intensive task will allow the other tasks a chance to run.

4 Likes